2013-04-10 58 views
9

我在比較類型的上限時,在Scala中激發使用類型類時遇到了一些麻煩。斯卡拉類型類的動機是什麼?

考慮下面的代碼:

case class NumList[T <: Complex](xs: Complex*) { 
    def sum = (xs fold new Complex(0, 0))(_ + _) 
    def map[U <: Complex](f: Complex => U): NumList[U] = NumList(xs.map(f): _*) 
    override def toString = "[" + xs.mkString(", ") + "]" 
    } 

    case class GenList[T](xs: T*) { 
    def sum(implicit num: Numeric[T]) = xs.sum 
    def map[U](f: T => U) = GenList(xs.map(f): _*) 
    override def toString = "[" + xs.mkString(", ") + "]" 
    } 

    val r = new Real(2) 
    val n = new Natural(10) 
    val comps = NumList(r, n, r, n) 

    println(comps) 
    println("sum: " + comps.sum) 
    println("sum * 2: " + comps.map(x => x + x).sum) 

    val comps2 = GenList(4, 3.0, 10l, 3d) 
    println(comps2) 
    println("sum: " + comps2.sum) 
    println("sum * 2: " + comps2.map(_ * 2).sum) 

雖然這兩個列表解決類似的問題,一個使用數字型級而另一個使用一個上限類型參數。我非常瞭解技術差異,但我很難理解類型類的核心動機。 目前爲止我發現的最佳動機如下:

雖然子類化或實現接口允許您使用大多數相同的設計,但type-classes允許您在每個方法的基礎上指定類型的功能,而通用類別的類型爲T,上限爲U約束T處處使用。考慮到這一點,type-classes提供了對泛型類中T的特性的更精細的控制。

是否有任何非常明確的例子來激發這種模式?

回答

9

試圖簡化一個主要方面,typeclasses嘗試收集獨立於您的類層次結構的行爲。

假設您需要定義一個新的數字類型MetaNum(使用標準數字操作),但不能或不會將它作爲Complex類型的子類,無論出於何種原因。

隨着Numeric typeclass,你只需要爲您的MetaNum提供一個適當的實例,提供所需的操作。

然後您可以創建一個GenList[MetaNum]並對其進行求和。

由於MetaNum不是Complex,所以不能使用NumList來做到這一點。在定義NumList時所做的實現選擇會在您嘗試在第二時間推廣操作/數據結構時重新啓動。

結論
類型類讓你更自由地從層次考慮獨立擴展你的行爲,在一些額外的複雜性和樣板成本。

我不能說你的意思是否與你的問題一樣。

+2

這是確切的推理。一般來說,類型類可以更簡潔,然後他們在Scala中。它們被編碼的方式允許它們以更靈活的方式使用,然後人們可以像Haskell這樣的東西,但Haskell的實現更加簡潔。另一個很好的例子是在你的層次結構中添加諸如序列化之類的東西,將接口添加到層次結構的頂部會破壞所有的舊類。使用類型類允許功能以增量方式實現,特別是只在需要的類型上實現。 – jroesch 2013-04-10 19:42:21

+2

就像@jroesch所說的,強調類型類在scala中作爲一種模式實現(即不是語言特性)的事實是有用的,但是這個概念是從其他語言(如haskell)中獲取的,其中嵌入了該特性語言本身。例如,haskell不是面向對象的語言,沒有繼承機制,所以類型特性用於通過自定義函數實現不同的數據類型來派生常見的功能。這就是爲什麼類型類在scala上可能看起來多餘的原因,在這裏有一個與對象繼承重疊的功能。 – 2013-04-11 14:41:52

+0

非常好。我獨自留下了一段時間,然後回到了自己的問題,試圖激勵它。我想出了一個非常類似的論點。太好了! – Felix 2013-05-27 10:24:14