2012-11-02 31 views
10

ho una caratteristica del genere:Scala - Co/Contra-varianza applicata alla selezione parametro implicito

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

Con una funzione che funziona con esso in questo modo:

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

Il intenzione è di fare qualcosa del genere:

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) 

Quindi è una classe di tipo per i tipi per cui l'ambiente sa già come piega e può essere definito per Ints, Stringhe, qualunque cosa.

mio problema è che voglio avere anche impliciti più specifici che hanno la priorità, in questo modo:

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) 

Comunque chiamata di metodo genera un conflitto, in quanto c'è un'ambiguità:

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

Quindi quello che voglio è che il compilatore cerchi l'implicito più specifico tra Any e il mio tipo. L'intento è quello di fornire implementazioni predefinite per tipi di base che possono essere sovrascritti facilmente per sottotipi più specifici, senza ombreggiatura che è brutta.

che sto potrebbero pensare wishfully qui, ma si può solo sperare :-)

risposta

14

L'approccio comune in una situazione come questa sfrutta il modo in cui impliciti sono priorità per eredità:

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

Ora l'istanza Set verrà utilizzata quando è applicabile, ma quella per Traversable è ancora disponibile quando non lo è.