2015-04-29 6 views
5

Ho giocato con lo schema di classe in Scala, ma non sono stato in grado di capire come implementare l'oggetto associato implicito quando il tipo con cui sto lavorando è generico.Typeclasses Scala con generici

Ad esempio, supponiamo di aver definito un tratto per un typeclass che fornisce le funzioni per inserire le cose in 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) 

Questo funziona come previsto, che mi permette di fornire implementazioni di Boxer di vari tipi. Tuttavia, ho no idea di come farei questo quando il tipo su cui vorrei agire è generico. Diciamo che volevo essere in grado di usare il mio Boxer su qualsiasi Seq[A]. object s in Scala non possono includere parametri di tipo, quindi sono in perdita per dove andare:

// 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 
} 

Esiste un modo per sostenere conversioni implicite di tipi generici senza dover implicitamente un oggetto separato per ogni tipo interno?

risposta

6

Sei davvero vicino, in realtà. È necessario rimuovere le parentesi da seqBoxer[A]. Altrimenti, il compilatore vede questa come una conversione implicita da () => Boxer[Seq[A]], piuttosto che semplicemente un implicito disponibile Boxer[Seq[A]]. Per buona misura, è anche una buona idea rendere esplicito il tipo di ritorno di un metodo implicito.

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)) 

si può effettivamente utilizzare questo stesso approccio per creare un generico Boxer[A] per qualsiasi A, dovrebbero essere tenuti a comportarsi allo stesso modo.

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! Mi ero completamente dimenticato della differenza tra 'def foo: A' e' def foo(): A'. Questo chiarisce davvero le cose! – KChaloux