-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Enum widening sabotages match types #16299
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
Comments
BTW. Enum widening also bit a few students of mine. They really didn't understand why
doesn't type check. It is "obvious" from looking at it, that it does not cause a run time error, so why does the compiler reject it? :) |
There were a lot of complaints before that widening for case classes did not happen. I think it was mostly from people used to ADTs that got bitten by the type inference to a subtype (which is of course not available for a classical ADT). So we made enums more like classical ADTs as a compromise. Note that this could be potentially solved by adding a way to specify "precise" type variables. Then the compiler would have enough information to not widen the type of |
Thanks @odersky for your quick response.
That is interesting to hear. I would have assumed that subtyping will just do its magic and in most cases it will go unnoticed.
This is a new feature you are proposing, right? Or do you say that the |
workaround: def resolve[S <: Source.Foo | Source.Bar](src: S): resolved[S] = ???
val foo: Target.Foo = resolve(Source.Foo(42)) // works I suggested at some point to add a |
Thanks Jamie, that's helpful! Any idea, why defining It is not really important, though, since I will use casts internally, anyway :) |
Can't tell why that doesn't work. That's exactly what match type return types are built for, so it's extra surprising. |
Currently, only match expressions with patterns of the form You could rewrite your example as: enum Target {
case Foo(n: Int)
case Bar(s: String)
}
enum Source {
case Foo(n: Int)
case Bar(s: String)
}
type resolved[T] = T match {
case Source.Foo => Target.Foo
case Source.Bar => Target.Bar
}
object Source {
type Cases = Source.Foo | Source.Bar
}
def resolve[S <: Source.Cases](src: S): resolved[S] = src match {
case src: Source.Foo => Target.Foo(src.n)
case src: Source.Bar => Target.Bar(src.s)
}
val foo: Target.Foo = resolve(Source.Foo(42)) |
FWIW, I checked the original example with the current import annotation.precise
enum Target {
case Foo(n: Int)
case Bar(s: String)
}
enum Source {
case Foo(n: Int)
case Bar(s: String)
}
type resolved[@precise T] = T match {
case Source.Foo => Target.Foo
case Source.Bar => Target.Bar
}
def resolve[@precise S <: Source](src: S): resolved[S] = ???
val foo: Target.Foo = resolve(Source.Foo(42)) //error |
Yet, considering that match types are a new thing, this really could be considered a bug and fixed without the need for any |
That's what I would've expected, as well. However, dropping the ascription results in the widening error:
|
At the risk of stating the obvious, but switching from enums to sealed traits and case classes would avoids all this, right? |
@dwijnand Yes :) |
It looks like widening of enums gets into the way when trying to use match types on them. I guess this is expected behavior, but it is still really sad.
Compiler version
Scala 3.2.0
Minimized example
Now, for
the match type gets stuck since
Foo
will be widened toSource
.Manually annotating works
but is infeasible in practice.
Discussion
Context: I want to use enums to model ADTs in a compiler. Different phases have different trees. Match types could really nicely tell which tree types in the source are translated to which in the next phase. Manually "downcasting" by ascribing the call does not work, since I literally would have to annotate over 1000 of such calls.
In general, I am not a big fan of the widening behavior. I do understand the design considerations, but overall would be happier without it.
(Please feel free to immediately close and mark as won't fix :) )
The text was updated successfully, but these errors were encountered: