Skip to content

Instantly share code, notes, and snippets.

@jg
Created December 8, 2012 09:42
Show Gist options
  • Save jg/4239603 to your computer and use it in GitHub Desktop.
Save jg/4239603 to your computer and use it in GitHub Desktop.
Typeclass Pattern
/** Notes from Seth Tisue 2012 NE Scala Symposium presentation
* Problem: decoupling, generic code that works for many types and is easily extensible
* Serialization example below
*/
case class Person(name: String, age: Int)
case class Restaurant(name: String, branch: Boolean)
/**
* 1. overloading
* - code duplication
*/
/*
def serialize(p: Person)
def serialize(r: Restaurant)
*/
/** 2. inheritance
* - coupling with each class we want to support
* - we need access to classes
*/
/*
trait class Serializable { def serialize: String}
class Person(...) extends Serializable {
override def serialize = ...
}
class Restaurant(...) extends Serializable {
override def serialize = ...
}
*/
/** 3. pattern matching on the type
* - methods need to know about all of the supported types
* - all of them will need modification
* - we need control over those methods
*/
/*
def serialize(x: Any) =
x match {
case p: Person => ...
case r: Restaurant => ...
def serializeToJSON(x: Any) =
x match { ...
*/
/** 4. typeclasses
* - static, typechecked
* - compiler must have all knowledge of the types
* - no dynamic dispatch on type (inheritance)
* - implicits complicate error messages
* - pattern boilerplate
* - performance overhead
* - harder to read
*/
case class Person(name: String, age: Int)
case class Restaurant(name: String, branch: Boolean)
trait Serializable[T] {
def ser(t: T): String
}
def serialize[T](t: T)(implicit s: Serializable[T]) = s.ser(t)
implicit object PersonIsSerializable extends Serializable[Person] {
def ser(p: Person) = "Person(" + p.name + ")"
}
implicit object RestaurantIsSerializable extends Serializable[Restaurant] {
def ser(p: Restaurant) = "Restaurant(" + p.name + ")"
}
serialize(Person("Frank", 40))
serialize(Restaurant("Speisekombinat", true))
implicit def ListIsSerializable[T: Serializable] = new Serializable[List[T]] {
def ser(xs: List[T]) = xs.map(serialize(_)).mkString("List(", ",", ")")
}
serialize(List(Person("Seth", 40), Person("Kaarin", 43)))
// Add pretty object method call syntax
implicit def addSerialize[T](t: T)
(implicit s: Serializable[T]) =
new { def serialize = s.ser(t) }
Person("Seth", 40).serialize
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment