2016-12-30 51 views
3

我有以下的情况下类:如何检查类型元组在编译时

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

+0

可能https://stackoverflow.com/questions/21442473/scala-generic-unzip-for-hlist#21444327 – Reactormonk

+0

签出类型约束:http://stackoverflow.com/questions/3427345/what-do-and-mean-in-scala-2-8-and-where-are-they-documented –

回答

5

您可以使用<:<类型类来证明ABTuple2的子类型,以便您可以分解它们。也就是说,我们可以编写一个unzip方法来使一些自由类型参数成为分解的纵坐标类型(称它们为A1,A2,B1B2)。那么,我们需要证据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 
        ^
+0

我的语义是什么值去哪里可能不是你想要的,但你可以重新安排他们以适应您的要求。 –

+0

我在哪里可以找到与<<<相关的文档,以及您称之为什么? – Omid

+1

@Omid http://www.scala-lang.org/api/current/scala/Predef$$$less$colon$less.html –

0

迈克尔的回答是优秀的。你也可以去一个更简单的途径,如果你愿意,要求A和B是子类型的产品在你的案件类的声明:

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) 
类型的

(任何,任何)。

我建议这种选择只是因为我不清楚你是多么复杂。我希望不要引起任何倒票;)