2015-04-29 16 views
5

Gram z wzorem typeclass w Scali, ale nie byłem w stanie wymyślić, jak zaimplementować niejawny obiekt towarzyszący, gdy typ, z którym pracuję jest generyczny.Scala Typeclasses z generics

Na przykład, powiedzmy, że zdefiniowałem cechę dla czcionki typograficznej, która zapewnia funkcje do umieszczania rzeczy w Box es.

case class Box[A](value: A) 

trait Boxer[A] { 
    def box(instance: A): Box[A] 
    def unbox(box: Box[A]): A 
} 

implicit object IntBoxer extends Boxer[Int] { 
    def box(instance: Int) = Box(instance) 
    def unbox(box: Box[Int]) = box.value 
} 

def box[A : Boxer](value: A) = implicitly[Boxer[A]].box(value) 
def unbox[A : Boxer](box: Box[A]) = implicitly[Boxer[A]].unbox(box) 

To działa zgodnie z oczekiwaniami, pozwalając mi dostarczyć implementacje Boxer dla różnych typów. Mam jednak pomysł, jak to zrobić, gdy typ, na którym chcę działać, jest generyczny. Powiedzmy, że chciałem móc użyć mojego Boxer na dowolnym Seq[A]. object s Scala nie może zawierać parametry typu, więc jestem ze stratą dla gdzie iść:

// Will not compile - object cannot have type arguments 
implicit object SeqBoxer[A] extends Boxer[Seq[A]] { ... } 

// Will not compile - 'A' is unrecognized 
implicit object SeqBoxer extends Boxer[Seq[A]] { ... } 

// Compiles but fails on execution, as this doesn't implement an implicit 
// conversion for _specific_ instances of Seq 
implicit object SeqBoxer extends Boxer[Seq[_]] { 
    def box(instance: Seq[_]) = Box(instance) 
    def unbox(box: Box[Seq[_]]) = box.value 
} 

// Will not compile - doesn't technically implement Boxer[Seq[_]] 
implicit object SeqBoxer extends Boxer[Seq[_]] { 
    def box[A](instance: Seq[A]) = Box(instance) 
    def unbox[A](box: Box[Seq[A]]) = box.value 
} 

// Compiles, but won't resolve with 'implicitly[Boxer[Seq[Foo]]]' 
// I had high hopes for this one, too :(
implicit def seqBoxer[A]() = new Boxer[Seq[A]] { 
    def box(instance: Seq[A]) = Box(instance) 
    def unbox(box: Box[Seq[A]]) = box.value 
} 

Czy istnieje jakiś sposób, aby wspierać niejawne konwersje typów generycznych bez konieczności niejawny oddzielny obiekt dla każdy typ wewnętrzny?

Odpowiedz

6

Jesteś naprawdę blisko. Musisz usunąć nawiasy z seqBoxer[A]. W przeciwnym razie kompilator widzi to jako niejawną konwersję z () => Boxer[Seq[A]], a nie po prostu dostępny niejawny Boxer[Seq[A]]. Na dobrą sprawę dobrze jest również podać wyraźny sposób zwracania niejawnej metody.

implicit def seqBoxer[A]: Boxer[Seq[A]] = new Boxer[Seq[A]] { 
    def box(instance: Seq[A]) = Box(instance) 
    def unbox(box: Box[Seq[A]]) = box.value 
} 

scala> box(Seq(1, 2, 3)) 
res16: Box[Seq[Int]] = Box(List(1, 2, 3)) 

Rzeczywiście można użyć tego samego podejścia do tworzenia ogólny Boxer[A] dla każdego A, powinny być zobowiązane zachowywać się w ten sam sposób.

implicit def boxer[A]: Boxer[A] = new Boxer[A] { 
    def box(instance: A): Box[A] = Box(instance) 
    def unbox(box: Box[A]): A = box.value 
} 

scala> box("abc") 
res19: Box[String] = Box(abc) 

scala> box(List(1, 2, 3)) 
res20: Box[List[Int]] = Box(List(1, 2, 3)) 

scala> unbox(res20) 
res22: List[Int] = List(1, 2, 3) 

scala> box(false) 
res23: Box[Boolean] = Box(false) 
+1

Ah! Całkowicie zapomniałem o różnicy między 'def foo: A' i' def foo(): A'. To naprawdę oczyszcza rzeczy! – KChaloux