如何解压元组中的元素类型?
How to unpack the type of elements in tuple in dotty?
假设我有一个 class 定义为:
case class Box[A](a: A)
我想编写一个通用方法,将元组 (Box[A1](a1), .., Box[An](an))
解包为类型为 (A1, .., An)
的元组 (a1, .., an)
。
我试过 Match Types
但没有成功:
scala> type Unpack[Bs <: Tuple] <: Tuple = Bs match {
| case Unit => Unit
| case Box[a] *: bs => a *: Unpack[bs]
| }
scala> def unpack[Bs <: Tuple](bs: Bs): Unpack[Bs] = bs match {
| case () => ()
| case Box(a) *: as => a *: unpack(as)
| }
2 | case () => ()
| ^^
|Found: Unit
|Required: Unpack[Bs]
|
|where: Bs is a type in method unpack with bounds >: Unit(?1) | Unit(?2) and <: Tuple
3 | case Box(a) *: as => a *: unpack(as)
| ^^^^^^^^^^^^^^^
|Found: A *: Unpack[Tuple]
|Required: Unpack[Bs]
|
|where: Bs is a type in method unpack with bounds >: (Any *: Tuple)(?3) and <: Tuple
我猜您是根据 the documentation 中的 Concat
示例测试了代码。我测试了这个例子......它没有在 0.21.0-RC1
.
上编译
dotr -version
Starting dotty REPL...
Dotty compiler version 0.21.0-RC1 -- Copyright 2002-2019, LAMP/EPFL
scala> type Concat[+Xs <: Tuple, +Ys <: Tuple] <: Tuple = Xs match {
| case Unit => Ys
| case x *: xs => x *: Concat[xs, Ys]
| }
1 |type Concat[+Xs <: Tuple, +Ys <: Tuple] <: Tuple = Xs match {
| ^^^^^^^^^^^^
|covariant type parameter Xs occurs in invariant position in Xs match {
| case Unit => Ys
| case
| [x, xs <: Tuple] => scala.internal.MatchCase[x *: xs, x *: Concat[xs, Ys]]
|} <: Tuple
如果我删除 +
以使其与您的示例相似,类型定义将通过:
type Concat[Xs <: Tuple, Ys <: Tuple] <: Tuple = Xs match {
| case Unit => Ys
| case x *: xs => x *: Concat[xs, Ys]
| }
scala>
但是我无法编写实现:
def concat[Xs <: Tuple, Ys <: Tuple](Xs: Xs, Ys: Ys): Concat[Xs, Ys] = Xs match {
| case () => Ys
| case x *: xs => x *: concat(xs, Ys)
| }
2 | case () => Ys
| ^^
|Found: (Ys : Ys)
|Required: Concat[Xs, Ys]
|
|where: Xs is a type in method concat with bounds >: (?1 : Unit) | (?2 : Unit) and <: Tuple
| Ys is a type in method concat with bounds <: Tuple
3 | case x *: xs => x *: concat(xs, Ys)
| ^^^^^^^^^^^^^^^^^^^
|Found: Any *: Concat[Tuple, Ys]
|Required: Concat[Xs, Ys]
|
|where: Xs is a type in method concat with bounds >: (?3 : Any *: Tuple) and <: Tuple
| Ys is a type in method concat with bounds <: Tuple
所以,让我们查阅文档。事情是......目前没有关于如何实施的文件。有a section telling us that things can be tricky.
那么它在实际代码中看起来如何? Concat
源代码中的实现 looks currently like this:
def dynamicConcat[This <: Tuple, That <: Tuple](self: This, that: That): Concat[This, That] = {
type Result = Concat[This, That]
// If one of the tuples is empty, we can leave early
(self: Any) match {
case self: Unit => return that.asInstanceOf[Result]
case _ =>
}
(that: Any) match {
case that: Unit => return self.asInstanceOf[Result]
case _ =>
}
val arr = new Array[Object](self.size + that.size)
// Copies the tuple to an array, at the given offset
inline def copyToArray[T <: Tuple](tuple: T, array: Array[Object], offset: Int): Unit = (tuple: Any) match {
case xxl: TupleXXL =>
System.arraycopy(xxl.elems, 0, array, offset, tuple.size)
case _ =>
tuple.asInstanceOf[Product].productIterator.asInstanceOf[Iterator[Object]]
.copyToArray(array, offset, tuple.size)
}
// In the general case, we copy the two tuples to an array, and convert it back to a tuple
copyToArray(self, arr, 0)
copyToArray(that, arr, self.size)
dynamicFromIArray[Result](arr.asInstanceOf[IArray[Object]])
}
当然,可能有人会争辩说这是出于性能原因(?),但似乎(在记录一些更好的方法之前)匹配类型的值只能使用 .asInstanceOf
。并且完全由您来确保值与推导的类型相匹配(yuk!):
scala> def unpack[Bs <: Tuple](bs: Bs): Unpack[Bs] = bs match {
| case () => ().asInstanceOf[Unpack[Bs]]
| case Box(a) *: as => (a *: unpack(as)).asInstanceOf[Unpack[Bs]]
| }
def unpack[Bs <: Tuple](bs: Bs): Unpack[Bs]
scala> unpack( ( Box(1), Box("test") ) )
val res0: Int *: Unpack[Box[String] *: Unit] = (1,test)
scala>
希望一些 Dotty 贡献者可以提出更好的解决方案,但到目前为止,这是我认为可行的唯一方法。
假设我有一个 class 定义为:
case class Box[A](a: A)
我想编写一个通用方法,将元组 (Box[A1](a1), .., Box[An](an))
解包为类型为 (A1, .., An)
的元组 (a1, .., an)
。
我试过 Match Types
但没有成功:
scala> type Unpack[Bs <: Tuple] <: Tuple = Bs match {
| case Unit => Unit
| case Box[a] *: bs => a *: Unpack[bs]
| }
scala> def unpack[Bs <: Tuple](bs: Bs): Unpack[Bs] = bs match {
| case () => ()
| case Box(a) *: as => a *: unpack(as)
| }
2 | case () => ()
| ^^
|Found: Unit
|Required: Unpack[Bs]
|
|where: Bs is a type in method unpack with bounds >: Unit(?1) | Unit(?2) and <: Tuple
3 | case Box(a) *: as => a *: unpack(as)
| ^^^^^^^^^^^^^^^
|Found: A *: Unpack[Tuple]
|Required: Unpack[Bs]
|
|where: Bs is a type in method unpack with bounds >: (Any *: Tuple)(?3) and <: Tuple
我猜您是根据 the documentation 中的 Concat
示例测试了代码。我测试了这个例子......它没有在 0.21.0-RC1
.
dotr -version
Starting dotty REPL...
Dotty compiler version 0.21.0-RC1 -- Copyright 2002-2019, LAMP/EPFL
scala> type Concat[+Xs <: Tuple, +Ys <: Tuple] <: Tuple = Xs match {
| case Unit => Ys
| case x *: xs => x *: Concat[xs, Ys]
| }
1 |type Concat[+Xs <: Tuple, +Ys <: Tuple] <: Tuple = Xs match {
| ^^^^^^^^^^^^
|covariant type parameter Xs occurs in invariant position in Xs match {
| case Unit => Ys
| case
| [x, xs <: Tuple] => scala.internal.MatchCase[x *: xs, x *: Concat[xs, Ys]]
|} <: Tuple
如果我删除 +
以使其与您的示例相似,类型定义将通过:
type Concat[Xs <: Tuple, Ys <: Tuple] <: Tuple = Xs match {
| case Unit => Ys
| case x *: xs => x *: Concat[xs, Ys]
| }
scala>
但是我无法编写实现:
def concat[Xs <: Tuple, Ys <: Tuple](Xs: Xs, Ys: Ys): Concat[Xs, Ys] = Xs match {
| case () => Ys
| case x *: xs => x *: concat(xs, Ys)
| }
2 | case () => Ys
| ^^
|Found: (Ys : Ys)
|Required: Concat[Xs, Ys]
|
|where: Xs is a type in method concat with bounds >: (?1 : Unit) | (?2 : Unit) and <: Tuple
| Ys is a type in method concat with bounds <: Tuple
3 | case x *: xs => x *: concat(xs, Ys)
| ^^^^^^^^^^^^^^^^^^^
|Found: Any *: Concat[Tuple, Ys]
|Required: Concat[Xs, Ys]
|
|where: Xs is a type in method concat with bounds >: (?3 : Any *: Tuple) and <: Tuple
| Ys is a type in method concat with bounds <: Tuple
所以,让我们查阅文档。事情是......目前没有关于如何实施的文件。有a section telling us that things can be tricky.
那么它在实际代码中看起来如何? Concat
源代码中的实现 looks currently like this:
def dynamicConcat[This <: Tuple, That <: Tuple](self: This, that: That): Concat[This, That] = {
type Result = Concat[This, That]
// If one of the tuples is empty, we can leave early
(self: Any) match {
case self: Unit => return that.asInstanceOf[Result]
case _ =>
}
(that: Any) match {
case that: Unit => return self.asInstanceOf[Result]
case _ =>
}
val arr = new Array[Object](self.size + that.size)
// Copies the tuple to an array, at the given offset
inline def copyToArray[T <: Tuple](tuple: T, array: Array[Object], offset: Int): Unit = (tuple: Any) match {
case xxl: TupleXXL =>
System.arraycopy(xxl.elems, 0, array, offset, tuple.size)
case _ =>
tuple.asInstanceOf[Product].productIterator.asInstanceOf[Iterator[Object]]
.copyToArray(array, offset, tuple.size)
}
// In the general case, we copy the two tuples to an array, and convert it back to a tuple
copyToArray(self, arr, 0)
copyToArray(that, arr, self.size)
dynamicFromIArray[Result](arr.asInstanceOf[IArray[Object]])
}
当然,可能有人会争辩说这是出于性能原因(?),但似乎(在记录一些更好的方法之前)匹配类型的值只能使用 .asInstanceOf
。并且完全由您来确保值与推导的类型相匹配(yuk!):
scala> def unpack[Bs <: Tuple](bs: Bs): Unpack[Bs] = bs match {
| case () => ().asInstanceOf[Unpack[Bs]]
| case Box(a) *: as => (a *: unpack(as)).asInstanceOf[Unpack[Bs]]
| }
def unpack[Bs <: Tuple](bs: Bs): Unpack[Bs]
scala> unpack( ( Box(1), Box("test") ) )
val res0: Int *: Unpack[Box[String] *: Unit] = (1,test)
scala>
希望一些 Dotty 贡献者可以提出更好的解决方案,但到目前为止,这是我认为可行的唯一方法。