In https://jto.github.io/articles/typelevel_quicksort :
We are exposed to a Sum
type whose apply
looks like this:
def apply
Types Sum[A, B]
and Sum.Aux[A, B, C] = Sum[A, B] { type Out = C }
are different. The latter is a subtype of the former. Also Sum[A, B]
is existential type Sum.Aux[A, B, _]
.
Wouldn't it be "obvious" that the return type of
apply
would have a Sum#Out type equal to sum.Out?
No,
def apply[A <: Nat, B <: Nat](implicit sum: Sum[A, B]) = sum
is the same as
def apply[A <: Nat, B <: Nat](implicit sum: Sum[A, B]): Sum[A, B] = sum
The thing is that firstly you define implicits: either inductively with sum1
, sum2
or simply
implicit val sum00: Aux[_0, _0, _0] = new Sum[_0, _0] { type Out = _0 }
implicit val sum01: Aux[_0, _1, _1] = new Sum[_0, _1] { type Out = _1 }
implicit val sum10: Aux[_1, _0, _1] = new Sum[_1, _0] { type Out = _1 }
implicit val sum11: Aux[_1, _1, _2] = new Sum[_1, _1] { type Out = _2 }
...
Then when you write
def apply[A <: Nat, B <: Nat](implicit sum: Sum[A, B]): Aux[A, B, sum.Out] = sum
knowing just A
and B
is enough for resolving implicit. And every defined implicit "knows" its specific C
. But if you return just Sum[A, B]
this C
will be forgotten.
You could define
def apply[A <: Nat, B <: Nat, C <: Nat](implicit sum: Aux[A, B, C]): Aux[A, B, C] = sum
but then you will have to call it specifying C
manually: Sum[_2, _3, _5]
.
If we remove it and we just use
val x = Sum[_0, _1]
, it looks fine, except addingval y = Sum[x.Out, _1]
will not work, saying the compiler couldn't find the implicit Sum.
Sure. x.Out
is no longer _1
, it's just some abstract x.Out
, and implicit can't be resolved.