Type-Classes in Scala (Type Class pattern)

10:04 PM Posted by Yuriy Polyulya , ,
Scala equivalent of Haskell's Type Classes.

  Manage generic constraints (constrains to type variables in parametrically polymorphics types) for wildcard types which are not in the same hierarchy (Char and String as an example).

  Will use scala imlicit value features and scope of it visibility:
  • Use trait TypeChecker for organising constraint hierarchy. Add sealed for guaranty that all subclasses defined in the same file as the class itself.
  • Combine all possible variants in the TypeCheacker companion object. Implicit objects for all possible substitution's types which extends trait TypeChecker (implicit object CharOk extends TypeChecker[Char]).
  • Define default constructor for generic type with implicit parameter of trait TypeChecher of generic parameter type (class MyGeneric[T](implicit ch: TypeChecker[T]) { ) or use Scala 2.8 syntactic sugar - Context Bounds*.
  • Use companion object for generic type for hide import of implicit object and define factory method or methods for generic type creation.

package my.experiments
package domain

import scala.annotation.implicitNotFound

@implicitNotFound(msg = "type ${T} is not supported.")
sealed trait TypeCheaker[T]

private object TypeChecker {
  implicit object CharOk extends TypeChecker[Char]
  implicit object StringOk extends TypeChecker[String]

class MyGeneric[T : TypeChecker] { // generic with constrains (context bound constrain)

    def test(x: T) = x match {
      case value: Char => "Char value: %s" format value
      case value: String => "String value: %s" format value
      case _ => "Unposible situation"

object MyGeneric { // companion object for MyGeneric class
  import TypeChecker._
  def apply[T: TypeChecker]() = new MyGeneric[T]()

Using sample:
package my.experiments
import domain.MyGeneric

object Main {
  def main(args: Array[String]) = {

    val a = MyGeneric[Char]

    val b = MyGeneric[String]
    println(b.test("a - a"))

//    val c = MyGeneric[Int] // compile error
//    println(c.test(100))

A little bit long implementation but short and elegant using code.

   Contex Bounds* - were introduced in Scala 2.8. They describe an implicit value and require a parametrized type (Numeric[T] as example).

def f[T : C](x: T) = h(x) // where h requires an implicit value of type C[T]

syntactic sugar for:

def f[T,C](x: T)(implicit im:C[T]) = h(x) // where h requires an implicit value of type C[T]