I'm trying to implement a bit sophisticated example of typeclasses in Scala: convert value of type T to string implemented as a library a user can extends for any type T. So here's the trait all the typeclasses should implement:
trait Printable[T] {
def asString(value: T): String
}
The function we're going to call to do the job:
object Printer {
def print[T](value: T)(implicit p:Printable[T]): String = p.asString(value)
}
Object with a series of typeclasses for some default types (only int&long here for example):
object SimpleFormats extends Formats
trait Formats {
implicit val intFormat = new Printable[Int] {
override def asString(value: Int) = value.toString
}
implicit val longFormat = new Printable[Long] {
override def asString(value: Long) = value.toString
}
}
Imagine that the user wants to extend our library for some other type, like Float and Double. So he creates another object, subclassing the first one. The idea here is to add new implicit members to the base object so the default collection of typeclasses can be extended for any number of user-supplied types:
object ExtendedFormats extends Formats {
implicit val floatFormat = new Printable[Float] {
override def asString(value: Float) = value.toString
}
implicit val doubleFormat = new Printable[Double] {
override def asString(value: Double) = value.toString
}
}
And finally, the main app using our library. There are two example functions doing operations on different types, so it's not possible to supply single specific implicit Printer[T]
as:
- there are multiple operations on multiple types
- the
Printer
does not know beforehand which types may be supplied by the user.
So the main app looks like this:
object Example {
// compiles OK, uses only default typeclasses
def simpleExample(implicit formats: Formats) = {
import formats._
Printer.print(42) + Printer.print(24L)
}
// compilation failed, cannot find Printable[Float]
// uses user-supplied formats
def extendedExample(implicit formats: Formats) = {
import formats._
Printer.print(42f) + Printer.print(31337.0)
}
def main(args: Array[String]): Unit = {
implicit val formats = ExtendedFormats
println(simpleExample)
println(extendedExample)
}
}
I see that scala compiler tries to import implicits from the Formats
, ignoring the fact that it is ExtendedFormats
actually.
Questions:
- Is there a way to import implicits from a subclass as described in the example?
- Is there a better solution for a batch of user-supplied typeclasses?