(moving what I posted on Gitter here.)
Hi Everyone,
I’m just reading a bit more about scala 3’s Union Type (Union Types | Scala 3 — Book | Scala Documentation),
and the doc states that “The compiler assigns a union type to an expression only if such a type is explicitly given.” – is there a way to for the compiler to eliminate a type from a union? I am trying to see how I can use dotty’s union type to simulate an open-union type. The compiler returns an Object instead of the rest of the union, and I am having a hard time.
For instance, If we look at the code below (extensible effects):
extension [R[_], E, A](eff: Eff[[A] =>> Reader[E, A] | R[A], A])
def runReader(value: E): Eff[R, A] =
handleRelay[[A] =>> Reader[E, A], R, A, A]
(a => Pure(a))
([T] => (r: Reader[E, T]) => (k: Arr[R, T, A]) => r match { case ReaderL() => k(value) })
(eff)
The return type is Eff[R, A]
where R
is in the union Reader[E, A] | R[A]
, what the compiler will do is return the supertype of R
as stated in the document, which in this case is Object
. So, the next function that will apply the output of runReader
will apply it to this type Eff[[X] => Object, A]
. Unless, I explicitly define a type for it.
so, the example below will not work because of what I stated above.
type MyEffects = [A] =>> Reader[Int, A] | Writer[String, A]
val interpreter: Eff[MyEffects, Int] = for
i <- Reader.ask[Int]
_ <- Writer.tell(s"Environment: ${i}")
yield i
val program =
interpreter
.runReader(100)
.runWriter // Will fail here because the compiler infer the return type of runReader as
// `Eff[[X] => Object, Int]` when it should be `Eff[[X] => Writer[String, X], Int]`
However, If I explicitly define the return type of runReader
, it will compile but it doesn’t compose well.
val program =
val x: Eff[[A] =>> Writer[String, A], Int] = interpreter.runReader(100)
x.runWriter
How can I achieve this using dotty’s union types? Or, is it just impossible?