I've been playing with the typeclass pattern in Scala, but I haven't been able to figure out how to implement the implicit companion object when the type I'm working with is generic.
For example, let's say I've defined a trait for a typeclass 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 implementations of Boxer
for various types. However, I have no idea how I would do this when the type I wish to act on is generic itself. Let's say I wanted to be able to use my Boxer
on any Seq[A]
. object
s in Scala cannot include type parameters, so I'm at a loss for 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 any way to support implicit conversions of generic types without having to implicit a separate object for each internal type?
You're really close, actually. You need to remove the parentheses from seqBoxer[A]
. Otherwise, the compiler sees this as an implicit conversion from () => Boxer[Seq[A]]
, rather than simply an available implicit Boxer[Seq[A]]
. For good measure, it is also a good idea to make the return type of an 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))
You can actually use this same approach to create a generic Boxer[A]
for any A
, should be required to behave the same way.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With