2012-11-02 84 views
10

聯合/孔特拉 - 方差我有一個特點是這樣的:斯卡拉 - 適用於隱含參數選擇

trait CanFold[-T, R] { 
    def sum(acc: R, elem: T): R 
    def zero: R 
} 

有與它的工作原理是這樣的函數:

def sum[A, B](list: Traversable[A])(implicit adder: CanFold[A, B]): B = 
    list.foldLeft(adder.zero)((acc,e) => adder.sum(acc, e)) 

的意圖是做這樣的事情:

implicit def CanFoldSeqs[A] = new CanFold[Traversable[A], Traversable[A]] { 
    def sum(x: Traversable[A], y: Traversable[A]) = x ++ y 
    def zero = Traversable() 
} 

sum(List(1, 2, 3) :: List(4, 5) :: Nil) 
//=> Traversable[Int] = List(1, 2, 3, 4, 5) 

因此,它是一個類型的類型,其中環境已知道如何摺疊,可以定義爲Ints,字符串,不管。

我的問題是,我想也有優先,這樣更加具體的implicits:

implicit def CanFoldSets[A] = new CanFold[Set[A], Set[A]] { 
    def sum(x: Set[A], y: Set[A]) = x ++ y 
    def zero = Set.empty[A] 
} 

sum(Set(1,2) :: Set(3,4) :: Nil) 
//=> Set[Int] = Set(1, 2, 3, 4) 

但是,這種方法的調用產生衝突,因爲有歧義:

both method CanFoldSeqs in object ... 
and method CanFoldSets in object ... 
match expected type CanFold[Set[Int], B] 

所以我想要的是編譯器搜索任何和我的類型之間最具體的隱含。目的是爲基本類型提供默認實現,這些實現可以被更容易地替換爲更具體的子類型,而不會產生很醜的陰影。

我可以一廂情願地想在這裏,但人們只能希望:-)

回答

14

在這樣的情況下通常的方法採用的是implicits被繼承優先級方式的優勢:

trait LowPriorityCanFoldInstances { 
    implicit def CanFoldSeqs[A] = new CanFold[Traversable[A], Traversable[A]] { 
    def sum(x: Traversable[A], y: Traversable[A]) = x ++ y 
    def zero = Traversable() 
    } 
} 

object CanFoldInstances extends LowPriorityCanFoldInstances { 
    implicit def CanFoldSets[A] = new CanFold[Set[A], Set[A]] { 
    def sum(x: Set[A], y: Set[A]) = x ++ y 
    def zero = Set.empty[A] 
    } 
} 

import CanFoldInstances._ 

現在,Set實例將在適用時使用,但Traversable的實例在其不可用時仍可用。