-
Couldn't load subscription status.
- Fork 1.4k
Add helpful implicit errors #4823
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
Add helpful implicit errors #4823
Conversation
a8977a3 to
5ccad87
Compare
|
|
||
| import scala.annotation.implicitNotFound | ||
|
|
||
| @implicitNotFound("\nOutput Type Mismatch\n expected: ${B}\n actual: ${A}\n\n") |
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.
What about something like:
This operator requires that the output type be a subtype of Option[Int] but the actual type String was not a subtype of Option[Int]
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 agree π Wasn't sure about the alignment / terseness. How about just the follow, so as not to repeat the expected type?
This operator requires that the output type be a subtype of Option[Int] but the actual type was String.
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.
| override def apply(a: A): B = subtype(a) | ||
| } | ||
|
|
||
| implicit def implNothing[B]: HasOutput[Nothing, B] = new HasOutput[Nothing, B] { |
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.
Do you need these second instances?
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 know, right? Some more Scala weirdness. I needed them for some reason. It couldn't find one for Nothing, which means it didn't trigger the CanFail warning, but instead said gave the implicit error for HasError.
| import scala.annotation.implicitNotFound | ||
|
|
||
| @implicitNotFound("\nOutput Type Mismatch\n expected: ${B}\n actual: ${A}\n\n") | ||
| sealed abstract class HasOutput[-A, +B] extends (A => B) |
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 we want to avoid using the term Has here given its existing definition as another data type? Something like IsSubtypeOutput or IsSubtypeOfOutput might be more descriptive and reads relatively well in infix position. Like implicit ev: A IsSubtypeOfOutput Option[B]. We could simplify if we didn't care about distinguishing the channels but I think that is helpful to indicate to the user where something went wrong.
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 crossed my mindβHas is accounted for. I like implicit ev: A IsSubtypeOfOutput Option[B] a lot.
|
Updated @adamgfraser, but I left STM and ZStream alone for now, as I'm not sure how completely those files are going to change. Though I suppose most of ZIO will as well :P |
|
@kitlangton Looks good! Yes I think we're going to have changes to everything to some extent so can't let that stop us. I think this is probably a good point to get feedback from others though and if we have alignment it will be easy to push to the streams and STM as well. |
|
Sounds good! I'll poke some people on Monday for feedback :) |
dc43f98 to
6e8bdaf
Compare
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.
Big like π
|
Have you considered using a type alias? |
|
@joroKr21 That's interesting! I didn't realize you could do that! Unfortunately it doesn't seem to work on Scala 3. import scala.annotation.implicitNotFound
@implicitNotFound("${A} is not a subtype of ${B}")
type IsSubtypeOf[-A, +B] = A <:< B
object Example {
implicitly[String IsSubtypeOf Int]
}
// Cannot prove that String <:< Int.Maybe the Scala 3 team can resolve that. It would certainly be nice to make the implementation of this lighter weight. I will open an issue. |
|
Ah, I think this may be resolved by scala/scala3#11823. I commented to confirm. |
Oh cool. Also had no idea that worked :P |
|
I totally agree. Hopefully they will address in Scala 3 but otherwise this is already pretty minimal and any new type machinery is going to be more heavy weight than what you have here. |
|
Him, though it looks like there may be some other issues on dotty right now: [info] compiling 106 Scala sources and 1 Java source to /home/circleci/project/core/jvm/target/scala-3.0.0-RC1/classes ...
[error] -- [E008] Not Found Error: /home/circleci/project/core/shared/src/main/scala/zio/ZIO.scala:934:18
[error] 934 | map(result => !result)
[error] | ^^^^^^^
[error] | value unary_! is not a member of A
[error] -- [E008] Not Found Error: /home/circleci/project/core/shared/src/main/scala/zio/ZIO.scala:942:13
[error] 942 | a => a.fold[ZIO[R, Option[E], Unit]](ZIO.succeedNow(()))(_ => ZIO.fail(None))
[error] | ^^^^^^
[error] | value fold is not a member of ANot sure if that's because of my changes. I'll have to take a look. It looks like maybe the implicit conversion isn't firing as it does with |
That's a shame. Having a different class means you can't convert back and forth. You are converting from |
Oh, good point! What if I make these type extend |
You could try, but that sounds like it would cause a circular dependency |
6e8bdaf to
9c3229e
Compare
9c3229e to
4542c68
Compare
I actually don't think this will be a problem, as this will only be used internally, and they can be derived from |
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.
Let's get this merged!
ZIO uses many implicit evidence constraints to implement methods on the ZIO trait that only work with particular subtypes. For instance,
ZIO#some, which requires that theAtype is optional, is defined as:This is a very cool technique. The only problem is that the error message requires the user to understand how this technique works, particularly the meaning of the
<:<symbol:This could be clearer. Basically, by making our own versions, we can give more ZIO specific compile errors:
We could make this more specific for particularly difficult to use messages, potentially linking to documentation. Overall, this is basically an extension of the pattern used by the
CanFailconstraint.