1

迄今为止,HList 中的“zip”函数的工作方式与 scala 中的 zip 函数或无类型列表类似。它将丢弃较长操作数中的元素以符合较短的元素:

val a = 1 :: 2 :: 3 :: HNil
val b = 4 :: 5 :: HNil

println(a.zip(b))
// this results in (1, 4) :: (2, 5) :: HNil

HList 没有等效或类似于“zipAll”函数的操作,如果它太短而无法与另一个操作数一起压缩,则它需要 2 个额外的参数来表示每个操作数的占位符。

是否可以为 shapeless.HList 或 scala 3 Tuple 执行此操作?实现这一点的简短方法是什么?

非常感谢您的意见。

4

1 回答 1

1

我遵循我在标准库的实现中看到的内容。

这个想法是我们有一个干净的类型化接口和一个高效的该接口的运行时实现。

所以,首先,我需要对ZipAll类型内部的信息进行编码:

type ZipAll [L <: Tuple, R <: Tuple, TL, TR] <: Tuple = (L, R) match {
  case (hl *: restl, hr *: restr) => (hl, hr) *: ZipAll[restl, restr, TL, TR]
  case (EmptyTuple, h *: rest) => (TL, h) *: ZipAll[EmptyTuple, rest, TL, TR]
  case (h *: rest, EmptyTuple) => (h, TR) *: ZipAll[rest, EmptyTuple, TL, TR]
  case (EmptyTuple, EmptyTuple) => EmptyTuple
}

这种特殊类型确保当我们使用 时ZipAll,相应的结果类型将是正确的。特别是,ZipAll使用标准类型 TL(如果左侧元组比右侧元组短)或 TR(反之亦然)创建一个填充缺失元素的元组。因此,所有下一个类型测试都是正确的:

summon[ZipAll[(Int, Int), (Int, Double, Int), String, Int] =:= ((Int, Int), (Int, Double), (String, Int))

summon[ZipAll[(Int, Double, Int), (Int, Int), String, Int] =:= ((Int, Int), ( Double, Int), (Int), Int)

现在,我们可以创建实现的类型签名(使用扩展方法):

extension[L <: Tuple](t: L) {
  def zipAll[R <: Tuple, TR, TL](
    r: R, endL: TL, endR: TR
  ): ZipAll[L, R, TL, TR] = ???
}

现在,我们可以创建一个不安全的实现zipAll

def runtimeZipAll(l: Tuple, r: Tuple, endL: Any, endR: Any): Tuple = (l, r) match {
  case (hl *: restl, hr *: restr) => (hl, hr) *: zipAll(restl, restr, endL, endR)
  case (EmptyTuple, hr *: restr) => (endL, hr) *: zipAll(EmptyTuple, restr, endL, endR)
  case (hl *: restl, EmptyTuple) => (hl, endR) *: zipAll(restl, EmptyTuple, endL, endR)
  case (EmptyTuple, EmptyTuple) => EmptyTuple
}

这应该隐藏在实现中。然后,我们可以使用以下命令强制执行该不安全实现的正确类型asInstanceOf...

def zipAll[R <: Tuple, TR, TL](r: R, endL: TL, endR: TR): ZipAll[L, R, TL, TR] = 
  runtimeZipAll(l, r, endL, endR).asInstanceOf[ZipAll[L, R, TL, TR]]

然后,我应该无缝地使用新功能:

val res: ((Int, Int), (Int, Int), (String, Int)) = (1, 2).zipAll((10, 20, 30), "hello", 20.0) // should be correctly typed

可能它不是迄今为止最好的实现(实际上内部细节不是类型安全的),但它是一种实现安全接口和不安全实现的模式(在类型安全和性能之间取得良好的平衡)。

这里是我迄今为止在 Scastie 中描述的内容的实现。

于 2022-02-01T09:21:04.217 回答