-
Couldn't load subscription status.
- Fork 1.4k
Add Meta Subtype to Cause #1706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Not to sound negative or anything, it's just that I don't have much experience with keeping binary compatibility and I really admire the effort. I also know that this has nothing to do with the fact that we are close to ZIO 1.0 but more with the fact that we would like to evolve this further, or in a completely different direction and this would allow for a smoother upgrade path. I also know that I'm not going to be involved with the review of this and maybe you shouldn't listen to my opinion, but I'd feel much more sure in the future "binary sustainability" if we took the time to set up some tooling that make this possible. I also think this would be beneficial to the releases post 1.0. As I said, I don't have much experience with this, but I'm pretty sure there are other Java and Scala libraries that include these types of checks in the toolchain. I'd also be willing to help, but not before two weeks from now, as I have exams. I'd like to know everyone's thoughts on this. Thanks. |
|
|
||
| private def e9 = prop { (c: Cause[String]) => | ||
| (Cause.stackless(c) must_== c) && | ||
| (c must_== Cause.stackless(c)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| } | ||
|
|
||
| // Meta is excluded completely from equals & hashCode | ||
| final case class Meta[E](cause: Cause[E], data: Data) extends Cause[E] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be time to make all these case classes / objects private so we have more flexibility to change Cause during the 1.x lifecycle. What do you think?
A basic fold method that hides Meta could be used to allow people to traverse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that makes sense. So then fold would go to needing to take a parameter for each constructor of Cause, because we couldn't allow people to pattern match against the Cause constructors, right?
| } | ||
| } | ||
|
|
||
| final case class Data private[Cause] (stackless: Boolean) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this is enough for binary backward compatibility (I don't think so, but I am not sure). You might want to make the Data class private too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to do that initially but ran into issues because Data is exposed when users pattern match against Meta. But maybe to your point above if we disallow that then we can make this completely private. We just need to make sure we give users enough functionality to work with Cause like in the ZStream snippet I updated..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did anyone say streams? 🤪
I'd be happy to move that snippet into Cause's companion object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iravid Awesome! 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamgfraser Good point. I'm not exactly sure what to do here, but I do know if we don't hide enough, we're gonna have real trouble changing functionality in 1.x. We'd need MiMa to test to for sure either way...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't I try make all the constructors private and implementing a new version of fold. Then we should be able to make Data private. I can also try to set up MiMa so we can test binary compatibility and then we can just turn it on when we go live with 1.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be fantastic! Thank you! 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to add MiMa but it looks like it needs to compare a current version against a released version. However, I was able to make the constructors and Data private so I think we should be in good shape from a binary compatibility perspective.
| final case class Data private[Cause] (stackless: Boolean) | ||
|
|
||
| object Data { | ||
| final def apply(stackless: Boolean): Data = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function is implied by using a case class isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're trying to preserve the ability to augment Data with additional fields while preserving binary compatibility. So the thought from @neko-kai, if I understood it correctly, was that by overriding these methods we could then forward them to a new version and keep the old version private for compatibility. But I admit I am still learning about this so I am more just trying to follow the advice of others with more expertise at this point.
| ) | ||
| } | ||
| case Meta(cause, data) => | ||
| if (data.stackless) cause match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to pass Data in from above, that way you can have a default for all Meta, and each level can override it. For example, you might have stack nested inside stackless .That means you want the stack on the inside part but not on the outside part. This logic requires top-down inheritance of parent settings, with child override.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could do that by changing the last case to:
case m @ Meta(_, _) => causeToSequential(m)The cases for all the other constructors that allow nesting already pass down the parent Meta. So then if you had nested Meta cases you would get to the Meta(Meta) branch and you would then trigger the rule that innermost scope takes precedence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced you can do it without a stack (either real stack or fake stack), as you have "edges" that should inherit parent behavior and "wedges" that have their own behavior, and you have to flip flop between them. However, we can fix this later, I am happy to get this merged in for 1.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we are definitely simplifying a little bit here in that we are throwing away the second branch of any Both or Then cases, which might themselves contain metadata indicating that they should be shown. But as you say we can clean this up later,
|
This is ready for another review. |
| `Both.equals` satisfies commutativity $e7 | ||
| """ | ||
| object CauseSpec | ||
| extends ZIOBaseSpec( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💪
| } | ||
|
|
||
| final case class Die(value: Throwable) extends Cause[Nothing] { | ||
| object Fail { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd make all the companion objects private, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need these if we want to expose the apply methods on them. Normally I wouldn't do this but there is a good amount of code that uses them, and then is a reserved keyword so we can't use the normal lower case smart constructor for that. I could delete the companion objects and replace with methods with capitalized names or we could go through and change everything, maybe using parallel and sequential for the smart constructors, though that is wordier.
| new Both(left, right) | ||
| } | ||
|
|
||
| private case class Data(stackless: Boolean) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be well-hidden from Scalac. 😆
| } | ||
|
|
||
| final case class Fail[E](value: E) extends Cause[E] { | ||
| private case class Fail[E](value: E) extends Cause[E] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private final?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
| private case class Meta[E](cause: Cause[E], data: Data) extends Cause[E] { | ||
| override final def hashCode: Int = cause.hashCode | ||
| override final def equals(obj: Any): Boolean = obj match { | ||
| case traced: Traced[_] => cause == traced |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| case traced: Traced[_] => cause == traced | |
| case traced: Traced[_] => cause == traced.cause |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Traced could probably be replaced by Meta, tbh...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
| * one exists. | ||
| */ | ||
| final def dieOption: Option[Throwable] = | ||
| fold(_ => None, t => Some(t), None)(_ orElse _, _ orElse _, (z, _) => z) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to always use named parameters with the new fold 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. The ordering of type parameters is definitely arbitrary other than that I tried to curry it so that the Z parameter could be inferred.
|
@jdegoes I updated this to have The one issue that this still leaves open is the constructors. Normally my preference would be to just expose lower case "smart" constructors (e.g. |
|
@iravid I added a |
|
@ghostdogpr Done. Yes, I think this is good to merge. Though now a couple of the builds on Scala.JS are failing. It looks unrelated, though I haven't seen these errors before: And later: |
|
Oh you fixed the problem with master 👍 |
|
Yes, we had a conflict when I removed the ZIO internal test suite dependency last night and then the test migrations we merged were relying on the implementation of |
|
Hmmm, looks like we're getting the same error. |
|
I think I see the problem. The previous version was not actually doing 100 runs on Scala.js, only on JVM. So when I switched it over we ended up doing way more runs of those tests on Scala.js, which I guess caused us to run out of heap? Anyway, let me adjust the settings so we get the same behavior as the original and push another change. |
|
@ghostdogpr Hmmm. Still not working, and always those same two builds.. I think there are a few things we can pursue:
|
|
Maybe try your first suggestion first? There wasn't any issue in #1862 but this one looks systematic so that's really weird. |
* Add ZSchedule.elapsed example to docs
* CircleCI improvements * CircleCI improvements, yaml fix * CircleCI improvements, yaml fix 2 * CircleCI improvements, yaml fix 3
* implement timeoutFork * address review comments
|
Note that this reverts the |
|
Yes, totally fine! Thanks for spotting it @adamgfraser |
* implement Cause.Meta * fix access levels * make case class final * address review comments * migrate tests to ZIO Test * address review comments * fix type * fix another typo * pass `Data` in from above * implement flatMap * implement flatten * implement unsandbox via flatten * apply nonFlaky only on JVM * use jvm test aspect more * try ignoring tests * start reenabling tests * remove unused import * reenable more tests * Add ZSchedule.elapsed example to docs (zio#1871) * Add ZSchedule.elapsed example to docs * ignore flaky supervision test (zio#1874) * CircleCI improvements (zio#1859) * CircleCI improvements * CircleCI improvements, yaml fix * CircleCI improvements, yaml fix 2 * CircleCI improvements, yaml fix 3 * Implement ZIO#timeoutFork (zio#1856) * implement timeoutFork * address review comments
Resolves #1684.
I set it up so that
Metais excluded forequalsandhashCode. Key things to look at would be:Causewithstackless. Right now it is pretty aggressive where we just show the first cause of parallel or sequential errors, don't print the stack trace of aThrowable, and don't print tracing information. So every failure is three of four lines saying a fiber failed, whether or not it was unchecked, a string representation of the error if available, and a line that no ZIO tracing is available.I also noticed that some of the tests in
CauseSpecweren't being run because there were two assertions on separate lines that weren't joined with a&&. 😱 Fortunately there were no failing tests after I fixed it.Copying @ioleo.