如何组合具有兼容类型的两个元组?
How to combine two tuples with compatible types?
假设我有两个元组,第一个是类型为 (V1, V2, .., Vn)
、
的值元组
第二个是类型为 (V1 => V1, V2 => V2, .., Vn => Vn)
.
的函数元组
现在我想将两个元组合并为 (f1(v1), v2(v2), .., fn(vn))
类型 (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")
我不知道如何在 scala 3.0 中获得它(即 dotty)。
编辑: 我查看了 shapeless 的源代码并得到了一个(部分工作)解决方案:
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)]
我不知道为什么 apply
方法的 return 丢失了它的类型。
这似乎可以解决问题
val res = List(Range(0, values.productArity).map(n => {
val arg = values.productElement(n)
val f = funs.productElement(n).asInstanceOf[(arg.type) => arg.type]
f.apply(arg)
})).map {
case Vector(a, b, c) => Tuple3(a, b, c)
}.head
来自 shapeless 的 zipApply
将保持类型安全,例如
import shapeless.syntax.std.tuple._
val values = (1, 2.0, "3")
val funs = ((i: Int) => 2 * i, (f: Double) => 2 * f, (s: String) => s * 2)
funs zipApply values
产出
res0: (Int, Double, String) = (2,4.0,33)
但是尝试使用 val values = ("1", "2.0", "3")
会产生编译时错误。
why the return of apply method lost its type
这是因为您丢失了类型细化(此行为在 Scala 2 和 Dotty 中类似)。
代码
given [Hv, Hr, V <: Tuple, F <: Tuple](given z: Zip[V, F]): Zip[Hv *: V, (Hv => Hr) *: F] = new Zip {
...
应该是
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] {
...
或使用 Aux
模式
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] {
...
在 0.21.0-RC1 中测试。
假设我有两个元组,第一个是类型为 (V1, V2, .., Vn)
、
第二个是类型为 (V1 => V1, V2 => V2, .., Vn => Vn)
.
现在我想将两个元组合并为 (f1(v1), v2(v2), .., fn(vn))
类型 (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")
我不知道如何在 scala 3.0 中获得它(即 dotty)。
编辑: 我查看了 shapeless 的源代码并得到了一个(部分工作)解决方案:
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)]
我不知道为什么 apply
方法的 return 丢失了它的类型。
这似乎可以解决问题
val res = List(Range(0, values.productArity).map(n => {
val arg = values.productElement(n)
val f = funs.productElement(n).asInstanceOf[(arg.type) => arg.type]
f.apply(arg)
})).map {
case Vector(a, b, c) => Tuple3(a, b, c)
}.head
zipApply
将保持类型安全,例如
import shapeless.syntax.std.tuple._
val values = (1, 2.0, "3")
val funs = ((i: Int) => 2 * i, (f: Double) => 2 * f, (s: String) => s * 2)
funs zipApply values
产出
res0: (Int, Double, String) = (2,4.0,33)
但是尝试使用 val values = ("1", "2.0", "3")
会产生编译时错误。
why the return of apply method lost its type
这是因为您丢失了类型细化(此行为在 Scala 2 和 Dotty 中类似)。
代码
given [Hv, Hr, V <: Tuple, F <: Tuple](given z: Zip[V, F]): Zip[Hv *: V, (Hv => Hr) *: F] = new Zip {
...
应该是
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] {
...
或使用 Aux
模式
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] {
...
在 0.21.0-RC1 中测试。