2012-11-06

Type-Classes in Scala (Type Class pattern)

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

Motivation: 
  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).

Implementation:
  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.

Code:
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]
    println(a.test('a'))

    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.

Note:
   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]