1
votes

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?
1
You might look into simulacrum for typeclasses.Arseniy Zhizhelev

1 Answers

2
votes

If you know in advance that your code needs Printable for Float and for Double then you might declare it directly:

def extendedExample(implicit floatPrintable: Printable[Float], doublePrintable: Printable[Double]) = {
  Printer.print(42f) + Printer.print(31337.0)
}

Or create a collection of Format-like traits:

trait FormatDouble {
  implicit val doubleFormat: Printable[Double]
}

and then use it to specify what types do you need:

def extendedExample(implicit formats: Formats with FormatDouble with FormatFloat) = {
  import formats._
  Printer.print(42f) + Printer.print(31337.0)
}