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
applywould 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.