2015-04-29 7 views
5

Я играл с шаблоном типа в Scala, но мне не удалось выяснить, как реализовать неявный объект-компаньон, когда тип, с которым я работаю, является общим.Scala Typeclasses с generics

Например, предположим, что я определил черту для класса, предоставляющего функции для помещения вещей в Box.

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) 

Это работает, как ожидалось, что позволило мне обеспечить реализацию Boxer для различных типов. Тем не менее, у меня есть no Идея, как я буду делать это, когда тип, в котором я хочу действовать, является общим. Предположим, я хотел иметь возможность использовать мой Boxer на любом Seq[A]. object s в Scala не может включать в себя параметры типа, так что я в недоумении для куда идти:

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

Есть ли способ поддерживать неявные преобразования родовых типов без неявного отдельного объекта для каждого внутренний тип?

ответ

6

Вы действительно близки, на самом деле. Вам нужно удалить круглые скобки от seqBoxer[A]. В противном случае компилятор видит это как неявное преобразование от () => Boxer[Seq[A]], а не просто доступное неявное Boxer[Seq[A]]. Для хорошей меры также рекомендуется сделать тип возвращаемого значения неявного метода явным.

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

Вы можете использовать этот же подход, чтобы создать общий Boxer[A] для любого A, должны быть обязаны вести себя таким же образом.

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

Ах! Я полностью забыл о различии между 'def foo: A' и' def foo(): A'. Это действительно очищает вещи! – KChaloux

Смежные вопросы