如何在编译时检查类型是否为元组

时间:2016-12-30 20:46:51

标签: scala

我有以下案例类:

case class MyClass[A,B](a:A, b:B)

我想添加一个像MyClass一样解压缩的函数,所以如果AB是元组类型,那么我想提取它们如下:

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函数,但这是唯一的方法吗?

2 个答案:

答案 0 :(得分:5)

您可以使用<:<类型类来证明ABTuple2的子类型,以便您可以对其进行分解。也就是说,我们可以编写一个unzip方法来获得一些自由类型参数,这些参数将是已分解的纵坐标类型(称为A1A2B1和{ {1}})。然后,我们需要B2A <:< (A1, A2)的证据。如果子类型关系为true,编译器将查找这些类型类的实例,我们可以使用它们来完成转换。也就是说,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))

答案 1 :(得分:0)

迈克尔的回答非常好。如果您愿意在案例类的声明中要求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)

类型(任意,任何)。

我建议这种替代方案只是因为我不清楚你是否愿意做多么复杂。我希望不要引起任何下来的选票;)