Type classes

The type class pattern is a ubiquitous pattern in Scala, its function is to provide a behavior for some type. You think of it as an "interface" in the Java sense. Here's an example.

scala> /**
     |  * A type class to provide textual representation
     |  */
     | trait Show[A] {
     |   def show(f: A): String
     | }
defined trait Show

This class says that a value of type Show[A] has a way to turn As into Strings. Now we can write a function which is polymorphic on some A, as long as we have some value of Show[A], so that our function can have a way of producing a String:

scala> def log[A](a: A)(implicit s: Show[A]) = println(s.show(a))
log: [A](a: A)(implicit s: Show[A])Unit

If we now try to call log, without supplying a Show instance, we will get a compilation error:

scala> log("a string")
<console>:14: error: could not find implicit value for parameter s: Show[String]
       log("a string")
          ^

It is trivial to supply a Show instance for String:

scala> implicit val stringShow = new Show[String] {
     |   def show(s: String) = s
     | }
stringShow: Show[String] = $anon$1@c0b694a

scala> // and now our call to Log succeeds
     | log("a string")
a string

This example demonstrates a powerful property of the type class pattern. We have been able to provide an implementation of Show for String, without needing to change the definition of java.lang.String to extend a new Java-style interface; something we couldn't have done even if we wanted to, since we don't control the implementation of java.lang.String. We use this pattern to retrofit existing types with new behaviors. This is usually referred to as "ad-hoc polymorphism".

For some types, providing a Show instance might depend on having some implicit Show instance of some other type, for instance, we could implement Show for Option:

scala> implicit def optionShow[A](implicit sa: Show[A]) = new Show[Option[A]] {
     |   def show(oa: Option[A]): String = oa match {
     |     case None => "None"
     |     case Some(a) => "Some("+ sa.show(a) + ")"
     |   }
     | }
optionShow: [A](implicit sa: Show[A])Show[Option[A]]

Now we can call our log function with a Option[String] or a Option[Option[String]]:

scala> log(Option(Option("hello")))
Some(Some(hello))

Scala has syntax just for this pattern that we use frequently:

def log[A : Show](a: A) = println(implicitly[Show[A]].show(a))

is the same as

def log[A](a: A)(implicit s: Show[A]) = println(s.show(a))

That is that declaring the type parameter as A : Show, it will add an implicit parameter to the method signature (with a name we do not know).