[英]How to check if types are Tuple in compile time
我有以下案例类:
case class MyClass[A,B](a:A, b:B)
我想向MyClass
添加一个像 unzip 这样的函数,所以如果A
和B
是元组类型,那么我想像下面这样提取它们:
val item = MyClass[(Int,String), (Int,String)]((2,"two"), (3,"three"))
val item_left = MyClass(item.a._1, item.b._1)
val item_right = MyClass(item.a._2, item.b._2)
我应该如何做到这一点并在编译时检查元组的类型? 我不想在伴随对象中定义它,我希望它是MyClass
一个函数。 我知道我可以定义一个implicit
函数,但这是唯一的方法吗?
您可以使用<:<
类型类来证明A
和B
是Tuple2
子类型,以便您可以分解它们。 也就是说,我们可以编写一个unzip
方法来拥有一些自由类型参数,这些参数将是分解后的纵坐标类型(称为A1
、 A2
、 B1
和B2
)。 然后,我们需要证明A <:< (A1, A2)
和B <:< (B1, B2)
证据。 如果子类型关系为真,编译器将找到这些类型类的实例,我们可以使用它们来完成转换。 也就是说, A <:< (A1, A2)
扩展了函数A => (A1, A2)
。
case class MyClass[A, B](a: A, b: B) {
def unzip[A1, A2, B1, B2](implicit
ev1: A <:< (A1, A2),
ev2: B <:< (B1, B2)
): (MyClass[A1, A2], MyClass[B1, B2]) = {
val (a1, a2) = ev1(a)
val (b1, b2) = ev2(b)
(MyClass(a1, a2), MyClass(b1, b2))
}
}
在行动:
scala> MyClass((2, "two"), (3, "three")).unzip
res6: (MyClass[Int,String], MyClass[Int,String]) = (MyClass(2,two),MyClass(3,three))
对于非元组:
scala> MyClass(1, 2).unzip
<console>:14: error: Cannot prove that Int <:< (A1, A2).
MyClass(1, 2).unzip
^
迈克尔的回答非常好。 如果您愿意在案例类的声明中要求 A 和 B 是 Product 的子类型,您也可以走一条更简单的路线:
case class MyClass[A <: Product, B <: Product](a:A, b:B) {
def item_left = (a.productIterator.toList(0), b.productIterator.toList(0))
// etc.
}
现在,你可以写:
val x = MyClass((2,"two"), (3,"three"))
x.item_left
这导致:
(2,3)
类型(任何,任何)。
我建议这个替代方案只是因为我不清楚你愿意有多复杂。 我希望不会引起任何反对票;)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.