我正在尝试将元组压缩在一起并使用匹配类型来获取结果压缩的确切类型。我有一个匹配类型和功能:
type Z[A <: Tuple, B <: Tuple] <: Tuple = (A, B) match
case (EmptyTuple, EmptyTuple) => EmptyTuple
case (a *: as, b *: bs) => (a, b) *: Z[as, bs]
def z[A <: Tuple, B <: Tuple](a: A, b: B): Z[A, B] = (a, b) match
case (EmptyTuple, EmptyTuple) => EmptyTuple
case (ah *: at, bh *: bt) => (ah, bh) *: z(at, bt)
但是,
z()
中的两种情况都会导致错误:
分别是Found: EmptyTuple.type Required: test.Tuples.Z[A, B]
和Found: (Any, Any) *: test.Tuples.Z[Tuple, Tuple] Required: test.Tuples.Z[A, B]
。我本以为这将是匹配类型的一个非常简单的应用,但显然我错了。我在这里缺少什么?
我还想将匹配类型和函数
z()
限制为具有相同长度的元组(例如如何在 scala 2 中使用 shapeless' Length
),但这也许是一个单独的问题。
编辑:
我设法让函数
z()
使用显式转换,但我仍然认为必须有一种方法来避免这种情况:
def z[A <: Tuple, B <: Tuple, M <: Int](a: A, b: B): Z[A, B] = (a, b) match
case (ah *: at, bh *: bt) => ((ah, bh) *: z(at, bt)).asInstanceOf[Z[A, B]]
case (EmptyTuple, EmptyTuple) => EmptyTuple.asInstanceOf[Z[A, B]]
另外,我也能够让长度方面适用于函数
z()
,但我很想知道是否a)有一种更清晰/更简洁的方法来实现这一点(也许不需要定义L
) 和 b) 如果有办法将 Z
的类型参数限制为相同长度的元组:
type L[T <: Tuple] <: Int = T match
case EmptyTuple => 0
case _ *: t => 1 + L[t]
type Z[A <: Tuple, B <: Tuple] <: Tuple = (A, B) match
case (EmptyTuple, EmptyTuple) => EmptyTuple
case (a *: as, b *: bs) => (a, b) *: Z[as, bs]
def z[A <: Tuple, B <: Tuple, M <: Int](a: A, b: B)(using L[A] =:= L[B]): Z[A, B] = (a, b) match
case (ah *: at, bh *: bt) => ((ah, bh) *: z(at, bt)).asInstanceOf[Z[A, B]]
case (EmptyTuple, EmptyTuple) => EmptyTuple.asInstanceOf[Z[A, B]]
println(z(1 *: true *: EmptyTuple, "seven" *: 9.8 *: EmptyTuple)) // <-- correctly zips tuples: ((1,seven),(true,9.8))
// println(z(1 *: EmptyTuple, "seven" *: 9.8 *: EmptyTuple)) // <-- results in compile-time error as desired: "Cannot prove that test.Tuples.L[(Int *: EmptyTuple.type)] =:= test.Tuples.L[(String, Double)]..."
编辑2: 所以实际上事实证明元组长度已经被限制为相等,否则匹配类型
Z
无法解析,所以我想问题只是如何避免 z()
中的强制转换。
目前匹配类型在价值层面有很多限制:
这种匹配表达式的特殊输入模式仅在以下情况下使用: 满足以下条件:
- 匹配表达式模式没有防护
- 匹配表达式受审者类型是匹配类型受审者类型的子类型
- 匹配表达式和匹配类型具有相同的事例数
- 匹配表达式模式都是类型化模式,这些类型
到匹配类型中对应的类型模式=:=
您的代码
type Z[A <: Tuple, B <: Tuple] <: Tuple = (A, B) match
case (EmptyTuple, EmptyTuple) => EmptyTuple
case (a *: as, b *: bs) => (a, b) *: Z[as, bs]
def z[A <: Tuple, B <: Tuple](a: A, b: B): Z[A, B] = (a, b) match
case (EmptyTuple, EmptyTuple) => EmptyTuple
case (ah *: at, bh *: bt) => (ah, bh) *: z(at, bt)
打破条件 4。
case (EmptyTuple, EmptyTuple)
和 case (ah *: at, bh *: bt)
不是键入模式。
尝试一下是有意义的
type Z[A <: Tuple, B <: Tuple] <: Tuple = (A, B) match
case (EmptyTuple, EmptyTuple) => EmptyTuple
case (a *: as, b *: bs) => (a, b) *: Z[as, bs]
def z[A <: Tuple, B <: Tuple](a: A, b: B): Z[A, B] = (a, b) match
case _: (EmptyTuple, EmptyTuple) => EmptyTuple
case t: (_ *: _, _ *: _) => t match
case (ah *: at, bh *: bt) => (ah, bh) *: z(at, bt)
但不幸的是,由于类型擦除,这不起作用
z((1, "a"), (true, 2.0)) // ()
实际上,
case _: (EmptyTuple, EmptyTuple)
和case t: (_ *: _, _ *: _)
只是case _: (_, _)
并且匹配所有内容。如果我们交换大小写,那么 z((1, "a"), (true, 2.0))
会抛出运行时异常 (java.lang.IndexOutOfBoundsException: 0
)。
以下嵌套匹配类型/模式匹配的方法似乎有效
type Z[A <: Tuple, B <: Tuple] <: Tuple = A match
case EmptyTuple => Z1[B]
case a *: as => Z2[a, as, B]
type Z1[B <: Tuple] <: Tuple = B match
case EmptyTuple => EmptyTuple
type Z2[A, As <: Tuple, B <: Tuple] <: Tuple = B match
case b *: bs => (A, b) *: Z[As, bs]
def z[A <: Tuple, B <: Tuple](a: A, b: B): Z[A, B] = a match
case _: EmptyTuple => z1(b)
case a1: (_ *: _) => a1 match
case a2 *: as => z2(a2, as, b)
def z1[B <: Tuple](b: B): Z1[B] = b match
case _: EmptyTuple => EmptyTuple
def z2[A, As <: Tuple, B <: Tuple](a: A, as: As, b: B): Z2[A, As, B] = b match
case b1: (_ *: _) => b1 match
case b2 *: bs => (a, b2) *: z(as, bs)
// compiles
summon[Z[(Int, String), (Boolean, Double)] =:= ((Int, Boolean), (String, Double))]
z((1, "a"), (true, 2.0)) // ((1,true),(a,2.0))
// doesn't compile
// summon[Z[(Int, String, Long), (Boolean, Double)] =:= ((Int, Boolean), (String, Double))]
// z((1, "a", 3L), (true, 2.0))
// Match type reduction failed since selector EmptyTuple.type
// matches none of the cases
// case b *: bs => (Long, b) *: Z[EmptyTuple.type, bs]
// doesn't compile
// summon[Z[(Int, String), (Boolean, Double, Long)] =:= ((Int, Boolean), (String, Double))]
// z((1, "a"), (true, 2.0, 3L))
// Match type reduction failed since selector Long *: EmptyTuple.type
// matches none of the cases
// case EmptyTuple => EmptyTuple