Type inference difficulties with `UndefOr[A | B]`
See original GitHub issueCompiler version
3.1.3-RC1-bin-20220323-fb7f900-NIGHTLY
Minimized code
UndefOr
is based on the Scala.js concept:
The minimized example below demonstrates a common situation when working with Scala.js facades.
//> using scala "3.1.3-RC1-bin-20220323-fb7f900-NIGHTLY"
type UndefOr[A] = A | Unit
extension [A](maybe: UndefOr[A])
def foreach(f: A => Unit): Unit =
maybe match
case () => ()
case a: A => f(a)
trait Foo
trait Bar
object Baz:
var booBap: Foo | Bar = _
def z: UndefOr[Foo | Bar] = ???
@main
def main =
z.foreach(x => Baz.booBap = x)
Output
$ scala-cli compile test.scala
Compiling project (Scala 3.1.3-RC1-bin-20220323-fb7f900-NIGHTLY, JVM)
[error] ./test.scala:21:31: Found: (x : Object)
[error] Required: Foo | Bar
[error] z.foreach(x => Baz.booBap = x)
[error] ^
Error compiling project (Scala 3.1.3-RC1-bin-20220323-fb7f900-NIGHTLY, JVM)
Compilation failed
Expectation
If we add an explicit type annotation to the last line:
- z.foreach(x => Baz.booBap = x)
+ z.foreach((x: Foo | Bar) => Baz.booBap = x)
then it compiles ok:
$ scala-cli compile test.scala
Compiling project (Scala 3.1.3-RC1-bin-20220323-fb7f900-NIGHTLY, JVM)
[warn] ./test.scala:9:12: the type test for A cannot be checked at runtime
[warn] case a: A => f(a)
[warn] ^
Compiled project (Scala 3.1.3-RC1-bin-20220323-fb7f900-NIGHTLY, JVM)
Is it possible for the compiler to make this inference by itself?
Issue Analytics
- State:
- Created a year ago
- Comments:7 (6 by maintainers)
Top Results From Across the Web
Type inference has usability problems
I argue that it may hinder comprehension and increase cognitive load. To see the discussion about this article, see the post on Hacker...
Read more >Better implicits for combinations of js.| and js.UndefOr? #2067
@sjrd is there are way to make Union types working with js.UndefOr types? Since both are implicits and scala only uses one conversion...
Read more >Working with objects
there are various setters for b , c and d . This is primarily because of type inference. For instance if setC was...
Read more >Type Inference | Tour of Scala
The compiler uses the types of the arguments of MyPair to figure out what type A and B are. Likewise for the type...
Read more >Inference | Classroom Strategies
This helps students understand the different types of information they use to ... as well as the steps to solving a math problem...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I don’t think it’s by design, we have some logic to try to preserve hard unions (that is: unions written by the user rather than inferred) through type inference already: https://github.com/lampepfl/dotty/blob/6540ad9154797164561281080b6a4e24a191691d/compiler/src/dotty/tools/dotc/core/TypeComparer.scala#L470-L478 But this is fragile: it only kicks in when the rhs of the type comparison is a type variable or an intersection type, whereas here the rhs is a union type (but one side of the union is also part of the lhs union, and the other is a type variable). More generally, I think this code is problematic since it means we record unions in lower bounds of type variables which violates the precondition of addConstraint: https://github.com/lampepfl/dotty/blob/6540ad9154797164561281080b6a4e24a191691d/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala#L651-L658 A possible alternative would be to add a Mode flag (or boolean var) we would set in TypeComparer to tell the ConstraintHandling logic to create hard unions instead of soft unions when refining a lower bound: https://github.com/lampepfl/dotty/blob/6540ad9154797164561281080b6a4e24a191691d/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala#L266 (
Type#|
creates soft unions by default)(This might also help with other issues where we currently don’t preserve unions: https://github.com/lampepfl/dotty/issues/14494) /cc @mbovel
Basically it does not seem to infer sub parts of a union. Smaller example: