2016-12-30 50 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) 
類型的

(任何,任何)。

我建議這種選擇只是因爲我不清楚你是多麼複雜。我希望不要引起任何倒票;)