2012-11-02 9 views
10

Mam cechę tak:Scala - Co/Contra-Wariancja w zastosowaniu do dorozumianego wyboru parametru

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

Dzięki funkcji, która współpracuje z nim tak:

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

The zamiarem jest zrobienie czegoś takiego:

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) 

Tak więc jest to klasa typu dla typów, dla których środowisko już wie, jak fold i może być zdefiniowany dla Ints, String, cokolwiek.

Moim problemem jest to, że chcę mieć również bardziej specyficzne implicits że mają pierwszeństwo, tak:

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) 

jednak, że wywołanie metody generuje konflikt, ponieważ istnieje dwuznaczność:

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

Tak więc chcę, aby kompilator szukał najbardziej konkretnego niejawnego między Dowolnym a moim typem. Celem jest dostarczenie domyślnych implementacji dla typów bazowych, które można łatwo przesłonić dla bardziej konkretnych podtypów, bez cieniowania, które jest brzydkie.

ja może być życzeniowo tu na myśli, ale można tylko nadzieję :-)

Odpowiedz

14

Zwykłe podejście w takiej sytuacji korzysta z okazji, że implicits są priorytetowe dziedziczenia:

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._ 

Teraz instancja Set zostanie użyta, gdy będzie miała zastosowanie, ale wersja Traversable będzie nadal dostępna, gdy jej nie ma.

Powiązane problemy