Scala 3:类型化元组压缩

问题描述 投票:0回答:1

我正在尝试将元组压缩在一起并使用匹配类型来获取结果压缩的确切类型。我有一个匹配类型和功能:

  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()
中的强制转换。

scala tuples pattern-matching scala-3 match-types
1个回答
2
投票

目前匹配类型在价值层面有很多限制:

这种匹配表达式的特殊输入模式仅在以下情况下使用: 满足以下条件:

  1. 匹配表达式模式没有防护
  2. 匹配表达式受审者类型是匹配类型受审者类型的子类型
  3. 匹配表达式和匹配类型具有相同的事例数
  4. 匹配表达式模式都是类型化模式,这些类型
    =:=
    到匹配类型中对应的类型模式

您的代码

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 

如何让匹配类型在 Scala 3 中正常工作

Shapeless3 和注释

© www.soinside.com 2019 - 2024. All rights reserved.