Generic Scala Typeclasses

I played with a typeclass template in Scala, but I was not able to figure out how to implement an implicit companion object when the type I am working with is shared.

For example, suppose I define a trait for a type class that provides functions for putting things into 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) 

This works as expected, allowing me to provide Boxer implementations for different types. However, I have no idea how I will do this when the type in which I want to act is common. Let's say I wanted to use my Boxer for any Seq[A] . object in Scala cannot include type parameters, so I don’t understand where to go:

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

Is there a way to support implicit conversions of generic types without having to use a separate object for each inner type?

+5
source share
1 answer

In fact, you are really close. You need to remove the parentheses from seqBoxer[A] . Otherwise, the compiler sees this as an implicit conversion from () => Boxer[Seq[A]] , and not just an available implicit Boxer[Seq[A]] . For a good measure, it is also useful to make the return type of the implicit method explicit.

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

In fact, you can use the same approach to create a generic Boxer[A] for any A that should behave the same.

 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) 
+6
source

All Articles