2012-11-01 17 views
7

Chcę określić typ klasy tak:Problemy z kontrawariancji w Scala

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

implicit object CanFoldInts extends CanFold[Int, Int] { 
    def sum(x: Int, y: Int) = x + y 
    def zero = 0 
} 

implicit object CanFoldSeqs extends CanFold[Traversable[_], Traversable[_]] { 
    def sum(x: Traversable[_], y: Traversable[_]) = x ++ y 
    def zero = Traversable() 
} 

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

Jednak problemem jest to, kiedy to zrobić otrzymuję Traversable[Any] i byłoby miło dostać Traversable[Int] zamiast :

scala> sum(List(1,2,3) :: List(4, 5) :: Nil) 
res10: Traversable[Any] = List(1, 2, 3, 4, 5) 

Co gorsza, nie mogę zdefiniować niejawna dla Traversable[Int] po zdefiniowaniu jeden dla Traversable[_], bo wtedy definicje by c ause niejednoznaczność. Po wyjęciu włosów straciłem.

Czy istnieje sposób, w jaki mogę zwrócić tę kwotę zamiast zamiast Traversable[Any]?

Patrząc jak sum() jest zdefiniowana na Seq w bibliotece Scala, widzę, że działa z Numeric, która jest niezmienna, ale chcę implementacje domyślne supertypes io wynik będzie inny niż wejście (tak samo jako krotność pracy) jest fajne.

Odpowiedz

12

Jedynym sposobem, wiem, aby dodać parametry typu do takich klas typu jest użycie def zamiast object:

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

scala> sum(List(1, 2, 3) :: List(4, 5) :: Nil) 
res0: Traversable[Int] = List(1, 2, 3, 4, 5) 
+0

Oh wow, i że działa! ... jednak jest to def, które zwraca instancję cechy, którą chcesz i miałem wrażenie, że niejawne params muszą być konkretnymi instancjami ... to jest niesamowite :-) –

+0

, więc dlaczego 'def' działa i' niejawny obiekt' nie? – goral

+1

@goral: Ponieważ 'object' nie może przyjmować parametrów typu. – sschaef