I want to implement a type safe function that can flatten nested tuple in scala 3.0.2.
The following code is what I got:
trait Flatten[P]:
type Q <: Tuple
def flatten(p: P): Q
trait LowPriorityFlatten:
type Aux[P, Q0] = Flatten[P] { type Q = Q0 }
def make[P, Q0 <: Tuple](f: P => Q0): Aux[P, Q0] = new Flatten[P] {
type Q = Q0
def flatten(p: P): Q = f(p)
}
given [H, P <: Tuple, Q <: Tuple](using ft: Aux[P, Q]): Aux[H *: P, H *: Q] =
make({ case h *: t => h *: ft.flatten(t)})
object Flatten extends LowPriorityFlatten:
given Aux[EmptyTuple, EmptyTuple] = make(e => e)
given [H <: Tuple, T <: Tuple, FH <: Tuple, FT <: Tuple](using fh: Aux[H, FH], ft: Aux[T, FT]): Aux[H *: T, Tuple.Concat[FH, FT]] =
make({ case h *: t => fh.flatten(h) ++ ft.flatten(t) })
def f1[P <: Tuple, Q <: Tuple](p: P)(using ft: Aux[P, Q]): Q = ft.flatten(p)
def f2[P <: Tuple](p: P)(using ft: Flatten[P]): ft.Q = ft.flatten(p)
And with some test:
scala> val r1 = Flatten.f1((1, (2, 3)))
val r1: Flatten.Aux[(Int, (Int, Int)), (Int, Int, Int)]#Q = (1,2,3)
scala> val r2 = Flatten.f2((1, (2, 3)))
val r2: (Int, Int, Int) = (1,2,3)
scala> val s1: (Int, Int, Int) = r1
val s1: (Int, Int, Int) = (1,2,3)
scala> val s2: (Int, Int, Int) = r2
val s2: (Int, Int, Int) = (1,2,3)
scala> val t1: (Int, Int, Int) = Flatten.f1((1, (2, 3)))
val t1: (Int, Int, Int) = (1,2,3)
scala> val t2: (Int, Int, Int) = Flatten.f2((1, (2, 3)))
-- Error:
1 |val t2: (Int, Int, Int) = Flatten.f2((1, (2, 3)))
| ^
| no implicit argument of type Flatten.Aux[(Int, (Int, Int)), Q] was found for parameter ft of method f2 in object Flatten
|
| where: Q is a type variable with constraint <: (Int, Int, Int)
| .
| I found:
|
| Flatten.given_Aux_*:_*:[Int, ((Int, Int) *: EmptyTuple.type), Q](Flatten.given_Aux_*:_Concat[H, T, FH, FT])
|
| But given instance given_Aux_*:_Concat in object Flatten does not match type LowPriorityFlatten.this.Aux[((Int, Int) *: EmptyTuple.type), Q].
My questions are:
- Why the type of
r1
(Flatten.Aux[(Int, (Int, Int)), (Int, Int, Int)]#Q
) is more redundant thanr2
; - Why
t1
works as expected butt2
fails?
Thanks for any help