2015-04-29 5 views
5

Ich habe mit dem Typklassenmuster in Scala gespielt, aber ich konnte nicht herausfinden, wie das implizite Companion-Objekt implementiert wird, wenn der Typ, mit dem ich arbeite, generisch ist.Scala Typeclasses mit Generics

Zum Beispiel, ich habe ein Merkmal für eine Klasse definiert, die Funktionen zum Einfügen von Dingen in Box es bietet.

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) 

Dies funktioniert wie erwartet, so dass ich Implementierungen von Boxer für verschiedene Typen zur Verfügung zu stellen. Allerdings habe ich keine Idee, wie ich dies tun würde, wenn der Typ, auf den ich handeln möchte, selbst generisch ist. Nehmen wir an, ich wollte meine Boxer auf jedem Seq[A] verwenden können. object s in Scala nicht Typparametern umfassen, so dass ich bei einem Verlust bin für wohin sie gehen:

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

Gibt es eine Möglichkeit implizite Konvertierungen von generischen Typen zu unterstützen, ohne jede für zu impliziten ein separates Objekt mit interner Typ?

Antwort

6

Sie sind wirklich nah dran, eigentlich. Sie müssen die Klammern von seqBoxer[A] entfernen. Andernfalls sieht der Compiler dies als implizite Konvertierung von () => Boxer[Seq[A]] und nicht einfach als eine verfügbare implizite Boxer[Seq[A]]. Es ist auch eine gute Idee, den Rückgabetyp einer impliziten Methode explizit zu machen.

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

Sie können tatsächlich diese gleiche Methode verwenden, einen generischen Boxer[A] für jeden A zu schaffen, soll die gleiche Art und Weise verhalten erforderlich.

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! Ich habe den Unterschied zwischen 'def foo: A' und' def foo(): A' total vergessen. Das räumt wirklich auf! – KChaloux