Scala Type Class with Context Bounds

Ingredients of a type class and its usage

  • an interface (the type class) with type parameter (trait LoggerWrapper)
  • a companion object for the interface, providing implementations (object LoggerWrapper)
  • a user trait to mixin the desired functionality using context bounds (UsingALogger)
  • and a class using it (MyMain)
/**
 * this is the type class
 * @tparam T type to "select" logger implementations
 */
@annotation.implicitNotFound(msg = "Cannot find LoggerWrapper type class for ${T}\n Please provide additional implementation")
trait LoggerWrapper[T] {
  def doLog(v: T)
}

/**
 * type class companion object with two different
 * implementations
 */
object LoggerWrapper {

  /**implementation for Int */
  implicit def getLoggerForInt = new LoggerWrapper[Int] {
    def doLog(v: Int) = println("Int implementation logging Value: " + v)
  }

  /**implementation for String */
  implicit def getLoggerForString = new LoggerWrapper[String] {
    def doLog(v: String) = println("String implementation logging value: " + v)
  }
}

/**
 * Mixin to provide logging functionality
 */
trait UsingALogger {
  /**"lookup" implementation using context bounds*/
  def log[T: LoggerWrapper](v: T) = implicitly[LoggerWrapper[T]].doLog(v)
}

/**
 * class that wants to use a logger
 */
object MyMain extends App with UsingALogger {

  /**
   * prints: "String implementation logging Value: MySTRING"
   */
  log("MySTRING")

  /**
   * prints: "Int implementation logging Value: 1234"
   */
  log(1234)

  /**
   * produces compile error:
   * error: Cannot find LoggerWrapper type class for Double
   * Please provide additional implementation
   * log(1234.5)
   */
  //log(1234.5)

}

prints out

String implementation logging value: MySTRING
Int implementation logging Value: 1234

or throws compiler error (comment in log(1234.5))

error: Cannot find LoggerWrapper type class for Double
Please provide additional implementation
log(1234.5)

Advantages:

  • type-safe injection
  • No extra framework (means Spring avoided)
  • Composability of dependencies and implementations
  • overwritable default implementations
  • better abstraction

Connect with me on Google+

Comments are closed.