Suppose I have two tuples, the first is a tuple of values with type (V1, V2, .., Vn)
,
the second is a tuple of functions with type (V1 => V1, V2 => V2, .., Vn => Vn)
.
Now I want to combine the two tuples as (f1(v1), v2(v2), .., fn(vn))
with type (V1, V2, .., Vn)
.
scala> val values = (1, 2.0, "3")
val values: (Int, Double, String) = (1,2.0,3)
scala> val funs = ((i: Int) => 2 * i, (f: Double) => 2 * f, (s: String) => s * 2)
val funs: (Int => Int, Double => Double, String => String) = ..
scala> val res = ??? // (2, 4.0, "33")
I have no idea how to get this in scala 3.0 (i.e. dotty).
EDIT: I look into the source code of shapeless and got a (partial work) solution:
scala> trait Zip[V <: Tuple, F <: Tuple]{ type R <: Tuple; def apply(v: V, f: F): R }
scala> given Zip[Unit, Unit]{ type R = Unit; def apply(v: Unit, f: Unit): Unit = () }
scala> given [Hv, Hr, V <: Tuple, F <: Tuple](given z: Zip[V, F]): Zip[Hv *: V, (Hv => Hr) *: F] = new Zip {
| type R = Hr *: z.R
| def apply(v: Hv *: V, f: (Hv => Hr) *: F): R = {
| f.head(v.head) *: z.apply(v.tail, f.tail)
| }
| }
scala> val values = (1, 2.0, "3")
val values: (Int, Double, String) = (1,2.0,3)
scala> val funs = ((i: Int) => 2 * i, (f: Double) => 2 * f, (s: String) => s * 2)
val funs: (Int => Int, Double => Double, String => String) = ..
scala> def apply[V <: Tuple, F <: Tuple](v: V, f: F)(given z: Zip[V, F]): z.R = z.apply(v, f)
def apply[V <: Tuple, F <: Tuple](v: V, f: F)(given z: Zip[V, F]): z.R
scala> apply(values, funs)
val res0:
Zip[(Int, Double, String), (Int => Int, Double => Double, String => String)]#R = (2,4.0,33)
scala> val res: (Int, Double, String) = apply(values, funs)
1 |val res: (Int, Double, String) = apply(values, funs)
| ^^^^^^^^^^^^^^^^^^^
|Found: ?1.R
|Required: (Int, Double, String)
|
|where: ?1 is an unknown value of type Zip[(Int, Double, String), (Int => Int, Double => Double, String => String)]
I do not known why the return of apply
method lost its type.
why the return of apply method lost its type
This is because you lost type refinement (this behavior is similar in Scala 2 and Dotty).
The code
given [Hv, Hr, V <: Tuple, F <: Tuple](given z: Zip[V, F]): Zip[Hv *: V, (Hv => Hr) *: F] = new Zip {
...
should be
given [Hv, Hr, V <: Tuple, F <: Tuple](given z: Zip[V, F]): (Zip[Hv *: V, (Hv => Hr) *: F] { type R = Hr *: z.R }) = new Zip[Hv *: V, (Hv => Hr) *: F] {
...
or with Aux
pattern
given [Hv, Hr, V <: Tuple, F <: Tuple](given z: Zip[V, F]): Zip.Aux[Hv *: V, (Hv => Hr) *: F, Hr *: z.R] = new Zip[Hv *: V, (Hv => Hr) *: F] {
...
Tested in 0.21.0-RC1.