2014-02-05 24 views
0

Scala中的列表是協變的(List[A+])。我發現這導致我比其他任何事情都更麻煩,而且我正在尋找一種方法在我的列表上實施類型不變性。下面應該給一個編譯錯誤:如何執行類型不變性?

scala> val l: List[Int] = List(1, 2, 3) 
l: List[Int] = List(1, 2, 3) 

scala> "string" :: l 
res0: List[Any] = List(string, 1, 2, 3) 

scala> 1.0 :: l 
res1: List[AnyVal] = List(1.0, 1, 2, 3) 

編輯:請注意,這是一個由例子,我想知道是否有通用的解決方案,對所有的Scala SeqSetMap工作,甚至任何採用類型參數的Trait。如果這是不可能的,唯一的選擇就是放棄Scala系列產品,如scalazpsp-view,那麼這就是答案。

回答

8

您可以指定結果類型:

val rescala> val result: List[Int] = "string" :: l 
<console>:8: error: type mismatch; 
found : String 
required: Int 
     val result: List[Int] = "string" :: l 
             ^

你也可以創建自己的不變的方法是這樣的:

def prepend[T1, T2](t: T1, l: List[T2])(implicit e: T1 =:= T2) = e(t) :: l 

prepend(0, l) 
// List[Int] = List(0, 1, 2, 3) 

scala> prepend("str", l) 
<console>:10: error: Cannot prove that String =:= Int. 
       prepend("str", l) 
        ^

隨着value classes你可以爲List創建不變的包裝沒有運行時罰款是這樣的:

case class InvariantList[T](l: List[T]) extends AnyVal { 
    def ::(t: T) = InvariantList(t :: l) 
} 

val l = InvariantList(1 :: 2 :: 3 :: Nil) 

0 :: l 
// InvariantList(List(0, 1, 2, 3)) 

scala> "str" :: l 
<console>:13: error: type mismatch; 
found : String 
required: Int 
       "str" :: l 
        ^

你也可以使用從scalaz不變的方法收集連接:

import scalaz._, Scalaz._ 

List(0) |+| List(1, 2, 3) 
// List(0, 1, 2, 3) 

Vector('a) |+| Vector('b, 'c) 
// Vector('a, 'b, 'c) 

scala> List("string") |+| List(1, 2, 3) 
<console>:14: error: type mismatch; 
found : Int(1) 
required: String 
       List("string") |+| List(1, 2, 3) 
            ^

注意(由@drexin提到的)有一個不變的列表scalaz:IList

+0

到處指定結果類型意味着我放棄了類型推斷。包裝/擴展scala.collection._的所有方法將花費大量時間,我需要在我的所有項目中攜帶此代碼... – OlivierBlanvillain

+0

@OlivierBlanvillain:您無需在任何地方指定結果類型。您可以將其指定爲所有方法和所有類成員的結果。所以你會以任何方式得到編譯錯誤。 – senia

+0

我總是設置返回類型,但我仍然設法編寫buggy代碼,因爲意外協變;) – OlivierBlanvillain