How Scala implicits provide underscore.js / jQuery-style augmentations

Scala implicits allow one to write libraries like jQuery or underscore.js that add methods to existing classes without the syntactic overhead. This is very quick to demonstrate.

Consider the useful pluck method that underscore.js adds to Javascript arrays, used like so:

> _([{foo: 'baz'}, {foo: 'bizzle'}]).pluck("foo")
["baz", "bizzle"]

The underscore function sort of lets us “pretend” that pluck is a method of arrays of objects. Here is a similar method addition in Scala using implicits.

object Pluck {
  implicit def withPluck[T](ms: Seq[Map[String, T]]) = new {
    def	pluck(field: String) = ms.map(_.get(field))
  }
}

And here’s how you use it:

$ sbt console
scala> import Pluck._
scala> Seq(Map("foo" -> "baz"), Map("foo" -> "bizzle")) .pluck("foo")
res0: Seq[Option[java.lang.String]] = List(Some(baz), Some(bizzle))

In Scala, you can trivially “add” a method to an existing class without modifying that class or adding syntactic overhead. One may debate whether this is a good idea to do very often, of course, but clearly underscore.js is simply adding methods one really would expect an array to have.

Heterogeneous Maps and Keys with Phantom Types in Scala

The term “heterogeneous collection” captures a few different ideas, most popularly a set or list where the elements may have different types. This post is not about those, but about a map (or dictionary, or key-value mapping, or whatever else you’d like to call it) where the values may be of different types, but the program does not lose that type information.

I’ve made a preliminary library from these code snippets at https://github.com/kennknowles/scala-heterogeneous-map. Let’s jump right in and do our imports

import java.net.URI

import org.scalacheck._
import org.scalacheck.Arbitrary._
import org.scalacheck.Prop._
import org.scalacheck.Pretty._

To define a heterogeneous map, instead of Map[Key, Value] as you would normally have in Scala, we need a different type per key. So we embed the type of the value into the key by using a type constructor (function from type to type like List[_] or Set[_]) as our generic parameter to the HMap trait. Scala has some rather idiosyncratic ways of writing higher-kinded types, but here is the definition of the trait:

trait HMap[TypedKey[_]] { self =>
  def get[T](key: TypedKey[T]) : Option[T]
  def put[T](key: TypedKey[T], value: T) : HMap[TypedKey]
}

You can already notice the principle feature: A key can only be used to put/get a value of the appropriate type. Exercise for the reader: Add iteration over key-value pairs.

For this essay I will just use a default implementation layered directly on the existing type of maps and use casting under the hood.

object HMap {
  private class WrappedMap[TypedKey[_]](m: Map[TypedKey[_], AnyRef])
  extends HMap[TypedKey] {
    def get[T](key: TypedKey[T]) = m.get(key).asInstanceOf[Option[T]]
    def put[T](key: TypedKey[T], value: T) =
      new WrappedMap(m + (key -> value.asInstanceOf[AnyRef]))
  }

  def empty[TypedKey[_]] : HMap[TypedKey] = new WrappedMap[TypedKey](Map())
}

Let’s see about playing with this in the console for a bit. I’ll need to improvise a type for the keys. I’ll use integers with an extra type attached for the type of the values they point to.

$ xsbt console

scala> case class TInt[T](i: Int)
defined class TInt

scala> val m = HMap.empty[TInt]
m: HMap[TInt] = HMap$WrappedMap@67bcdb3f

scala> m.put(TInt[String](3), "hello")
res0: HMap[TInt] = HMap$WrappedMap@13216ee9

scala> m.put(TInt[Int](3), "goodbye")
<console>:11: error: type mismatch;
  found   : TInt[Int]
  required: TInt[Any]
  Note: Int <: Any, but class TInt is invariant in type T.
  You may wish to define T as +T instead. (SLS 4.5)
              m.put(TInt[Int](3), "goodbye")
                             ^

scala> m1.get(TInt[String](3))
res2: Option[String] = Some(hello)

scala> m1.get(TInt[String](5))
res3: Option[String] = None

scala> m1.get(TInt[Int](3))
res4: Option[Int] = Some(hello)

What? That last one is definitely wrong. Read on for why and how to fix it. The problem is the Java/Scala/JVM approach to equality – below I solve it via TInt, not HMap.

As was clear in the trait, the type parameter of TInt has to match from key to value. The type parameter is also compile-time only; a good compiler can eliminate the boxing/unboxing, in general. There is probably some object-oriented reason why Scala is not allowed to, but I didn’t check the bytecode.

Now the thing I did with TInt is a well-known pattern called “Phantom Types” (discussed by Foursquare, Neil Mitchell, Ralf Hinze, James Iry, and you should really just search for it and read as much as you like) so I will define a general phantom-type-augmented value. It is basically as simple as “case class WithPhantom[T, Phantom](v: T)” but there are some wrinkles surrounding equality, as we saw earlier. Are equal values boxed with the different phantom types equal? Clearly they should not be, but the best way to implement this was not obvious to me. I would love suggestions on improving these bits. (Direct them to the previously-mentioned github project for this code)

case class WithPhantom[T, Phantom: Manifest](v: T) {
  private val m = implicitly[Manifest[Phantom]]

  override def equals(other: Any) =
    other.isInstanceOf[WithPhantom[T, Phantom]] && {
      val otherPh = other.asInstanceOf[WithPhantom[T, Phantom]]
      (otherPh.m.erasure == this.m.erasure) && (otherPh.v == this.v)
    }

  override def hashCode =
    (v, implicitly[Manifest[Phantom]].hashCode).hashCode
  
  override def toString = "WithPhantom[%s](%s)"
    .format(implicitly[Manifest[Phantom]].erasure.getName, v)
}

Now we can use this to have TInt, TString, and, my favorite, TURI (think of a mime type registry for a crawler)

object WithPhantom {
  type TInt[T] = WithPhantom[Int, T]
  type TString[T] = WithPhantom[String, T]
  type TURI[T] = WithPhantom[URI, T]

  def TInt[T: Manifest](i: Int) = WithPhantom[Int, T](i)
  def TString[T: Manifest](str: String) = WithPhantom[String, T](str)
  def TURI[T: Manifest](uri: URI) = WithPhantom[URI, T](uri)
}

It is fun and all to play around in the scala console, but to make this robust, small as it is, I’ll use ScalaCheck properties. For that, I will need Arbitrary values for WithPhantom and HMap. This is somewhat problematic because to have an arbitrary HMap I need to choose arbitrary types which is another discussion entirely. So for these properties, I just use two types and request that the invoker of the functions ensure that they are different if needed, providing an arbitrary instance that just generates values that of types Int and String.

object ScalaCheckInstances {

  implicit def arbWithPhantom[T: Arbitrary, Phantom: Manifest]
    : Arbitrary[WithPhantom[T, Phantom]] =
    Arbitrary(for(v <- arbitrary[T]) yield WithPhantom[T, Phantom](v))

  def genHMap[Value1, Value2, TypedKey[_]](
    implicit arbV1: Arbitrary[Value1],
             arbV2: Arbitrary[Value2],
             arbK1: Arbitrary[TypedKey[Value1]],
             arbK2: Arbitrary[TypedKey[Value2]]): Gen[HMap[TypedKey]] = {
      for {
        kv1List <- arbitrary[List[(TypedKey[Value1], Value1)]]
        kv2List <- arbitrary[List[(TypedKey[Value2], Value2)]]
      } yield {
        var hmap = HMap.empty[TypedKey]
        for ((k, v) <- kv1List) { hmap = hmap.put(k, v) }
        for ((k, v) <- kv2List) { hmap = hmap.put(k, v) }
        hmap
      }
  }

  // For arbitrary, I just choose Int and String as the two phantom types and TInt for the key
  implicit def arbHMap[TypedKey[_]](
    implicit arbKInt: Arbitrary[TypedKey[Int]],
             arbKString: Arbitrary[TypedKey[String]]) =
    Arbitrary(genHMap[Int, String, TypedKey])
}

The most important test is that WithPhantom[Int, Int](x) != WithPhantom[Int, String](x) because that is the code I was least confident in. My pattern is to define properties without forAll invocations and separately register those properties with the testing framework. As before, generating arbitrary types is beyond the scope of this essay, so I just ask the caller of the prop_typeMiss to provide different types.

object WithPhantomProperties extends Properties("WithPhantom") { 
  // Types must be different
  def prop_typeMiss[T, Value1: Manifest, Value2: Manifest](x: T) : Prop =
    WithPhantom[T, Value1](x) != WithPhantom[T, Value2](x)

  property("typeMiss") =
    forAll { x:Int => prop_typeMiss[Int, Boolean, String](x) }
}

In the github project, you can run these via “xsbt test“, but if you are just following along by copying this into a .scala file, then the easiest way is via “xsbt console“.

$ xsbt console

WithPhantomProperties.check
+ WithPhantom.typeMiss: OK, passed 100 tests.

And then the spec for HMap is more-or-less the same as the spec for Map, but we add an extra check to make sure that lookups in the map for different phantom types also miss.

object HMapProperties extends Properties("HMap") { 
  import ScalaCheckInstances._
  import WithPhantom._ // for TInt, etc

  def prop_empty[TypedKey[_], T](x: TypedKey[T]) : Prop = 
    HMap.empty.get(x) ?= None

  def prop_hit[TypedKey[_], T](m: HMap[TypedKey], x: TypedKey[T], v: T) : Prop = 
    m.put(x, v).get(x) ?= Some(v)

  def prop_miss[TypedKey[_], T](m: HMap[TypedKey], x: TypedKey[T],
                                y: TypedKey[T], v: T) : Prop =
      (x != y) ==> (m.put(y, v).get(x) ?= m.get(x))
    
  // When x == y but T != U
  def prop_typeMiss[TypedKey[_], T, U](m: HMap[TypedKey], x: TypedKey[T],
                                       y: TypedKey[U], v: U) : Prop =
    m.put(y, v).get(x) ?= m.get(x)

  property("empty") = forAll { x:TInt[Int] => prop_empty(x) }

  property("hit") = forAll { (m:HMap[TInt], x: TInt[Int], v: Int) => prop_hit(m, x, v) }
  
  property("miss") = forAll { (m:HMap[TInt], x: TInt[Int], 
                               y: TInt[Int], v: Int) =>
    prop_miss(m, x, y, v)
  }
  
  property("typeMiss") = forAll { (m:HMap[TInt], x: Int, v: Boolean) => 
    prop_typeMiss(m,
                  WithPhantom[Int, Int](x).asInstanceOf[TInt[Int]],
                  WithPhantom[Int, Boolean](x).asInstanceOf[TInt[Boolean]], 
                  v) }
}

And try it in the console

$ xsbt console

scala> HMapProperties.check
+ HMap.empty: OK, passed 100 tests.
+ HMap.hit: OK, passed 100 tests.
+ HMap.miss: OK, passed 100 tests.
+ HMap.typeMiss: OK, passed 100 tests.

All the checks pass! I do have some lingering doubts about equality and hashcodes…

I’m not claiming novelty of idea or implementation; I have simply written these thoughts up as an exercise for myself and hopefully entertainment for you. Here are some other references to similar ideas; please share other related links in the comments.

Functional Reactive Programming Concepts in Javascript on top of Backbone

Functional Reactive Programming, or FRP, is an elegant approach to “purely functional” event-driven programming with values that change over time. It is a change of perspective from the usual meaning of “event driven” in Javascript, and it is very, very cool.

There already exists a from-first-principles implementation of FRP in Javascript via the Flapjax compiler, a lot of related ideas are in knockout.js, and Asana says their Luna framework was inspired by FRP. But I want to demonstrate how simple it is to get a naive implementation of FRP off the ground by using the event-handling API provided by Backbone. Since we are in Javascript, the semantic work that has gone into FRP is pretty much out the window anyhow.

Let’s dive in; here’s the HTML shell I’m going to use:

<!doctype html>
<html lang="en">
  <head>
    <title>Shortest Path to FRP benefits in Javascript</title>

    <script src="jquery.js"></script>
    <script src="underscore.js"></script>
    <script src="mustache.js"></script>
    <script src="ICanHaz.js"></script>
    <script src="backbone.js"></script>
    <script src="reactive.js"></script>

    <script type="text/html" id="icanhaz-example">
      <ol>
      {{#list}}
      <li>{{value}}</li>
      {{/list}}
      </ol>
    </script>
  </head>
  <body>
    <div>Date and time: <span id="datetime"></span></div>
    <div>Deciseconds: <span id="deciseconds"></span></div>
    <div>Seconds: <span id="seconds"></span> and <span id="seconds2" /></div>
    <div>Seconds even: <span id="secondsEven"></span></div>
    <div>Stuttering: <span id="stutter"></span></div>
    <div>I can haz reactivity: <div id="icanhaz-output"></div></div>
  </body>
</html>

You can get jquery, backbone, ICanHaz, mustache, and underscore however you like. I just tossed them in the directory for this demo, here: http://kennknowles.github.com/codeslashslashcomment/2011-12-28-ReactiveJS/reactive.html

$(document).ready(function() {

FRP is based on two main semantic types that are very closely related: event streams and behaviors. Event streams are infinite sequences of data attached to a time, where time is nondecreasing. Behaviors are variables whose value changes continuously over time, i.e. functions of time. In pseudo-Haskell:

// EventStream a = [(Time, Event)]
// Behavior a = Time -> a

But “Time” is not a type we actually have available, so we keep these types abstract and expose primitives and combinators. There are many implementation choices available, the latest and greatest is described in Push-Pull Functional Reactive Programming by Conal Elliott, but since we are in a mutatey language already that has some event handling, the best reference is probably this draft of an imperative implementation strategy.

One major takeaway from FRP is make the event stream first class, so let us start with defining that class. It is quite literally just a handle for listening for occurrences of events. In actual use, you have to deal with the fact that listeners should be weak references, but that is a battle for another day.

    var EventStream = function() { };
    _.extend(EventStream.prototype, Backbone.Events, {
        _listen: function(callback) {
            this.bind("occur", callback);
        },

        _unlisten: function(callback) {
            this.unbind("occur", callback);
        },

        _occur: function(payload) {
            this.trigger("occur", payload);
        }
    });

The _listen and _occur methods expose the implementation of EventStream. If you are calling them, then you are not really using FRP, but implementing FRP, and your code would not necessarily be portable to a different implementation strategy.

The most obvious event stream (to me?) is a heartbeat that says the time. From this we can build useful things like pseudo-continuous time. This does violate an FRP notion of time invariance where a program always behaves the same when shifted in time, but oh well!

    var timerE = function(delay) {
        var stream = new EventStream();
        setInterval(function() {
            stream._occur(new Date());
        });
        return stream;
    }

An implementation for Behaviors is simply a boxed variable with similar event-handling routines. Again, no client code should call _change or _observe.

    var Behavior = function(initialValue) { 
        this.value = initialValue;
    }
    _.extend(Behavior.prototype, Backbone.Events, {
        _change: function(newValue) {
            this.value = newValue;
            this.trigger("change", newValue);
        },

        _observe: function(fn) {
            this.bind("change", fn);
        },

        _unobserve: function(fn) {
            this.unbind("change", fn);
        }
    });

A first primitive form of behavior is the stepper: Starting with some initial value, it listens to an event stream and saves the values that come in.

    var stepperB = function(initialValue, stream) {
        var behavior = new Behavior(initialValue);
        stream._listen(function(eventValue) {
            behavior._change(eventValue);
        })
        return behavior;
    }

And now we can make a behavior that counts time upwards

    var timeB = function(init, granularity) { return stepperB(init, timerE(granularity)); };

That is a taste of how we build behaviors and event streams without going under the hood, but we will need more primitives. In particular we will definitely need to be able to map a function over an event stream or behavior.

    var mapE = function(f, stream) {
        var mappedE = new EventStream();
        stream._listen(function(ev) {
            mappedE._occur(f(ev));
        });
        return mappedE;
    }

    var mapB = function(f, behavior) {
        var mappedB = new Behavior(behavior.value);
        behavior._observe(function(v) {
            mappedB._change(f(v));
        });
        return mappedB;
    }

Hmmm, those look very similar. Indeed, we forgot to implement the inverse of stepper, and then we would only need one of mapB and mapE. I’ll add a few more primitives here.

    var changesE = function(behavior) {
        var stream = new EventStream();
        behavior._observe(function(value) {
            stream._occur(value);
        });
        return stream;
    }
    
    var mapB_2 = function(f, behavior) {
        return stepperB(behavior.value, mapE(f, changesE(behavior)));
    }

    var filterE = function(p, stream) {
        var filtered = new EventStream();
        stream._listen(function(event) {
            if (p(event)) filtered._occur(event);
        });
        return filtered;
    }

    var snapshotE = function(behavior, stream) {
        var snapshots = new EventStream();
        stream._listen(function(event) {
            snapshots._occur(behavior.value); // bad? probably
        });
        return snapshots;
    }

And this all gets to be the most fun when it is higher-order. The switcherB starts as one behavior, but listens for new ones on an event and switches over to them. This is also a primitive.

    var switcherB = function(initialB, behaviorsE) {
        var b = new Behavior(initialB.value);
        var currentB = initialB;
        var callback = function(value) {
            b._change(value);
	}
        currentB._observe(callback);
        behaviorsE._listen(function (newB) {
            currentB._unobserve(callback);
            currentB = newB;
            currentB._observe(callback);
            b._change(currentB.value);
        });
        return b;
    }

To actually see this stuff, we need a “legacy” adapter to the browser’s imperatively-updated DOM. In this implementation, we can actually abuse mapB for this, but that is not in the spirit of the function, so we’ll drop to primitives again.

    var bindB = function(elem, behavior) {
        behavior._observe(function(value) {
            $(elem).html(value);
        });
    }

One thing to note is that the framework does nothing to minimize the amount of event passing that happens. You’ve got filterE and snapshotE to do that, but it can take some ingenuity to make sure the same value isn’t pushed over and over. I imagine there are solutions and/or wisdom in the literature which I have simply neglected in this quick hack.

Anyhow, we can now bind a bunch of behaviors to the DOM and watch them go.

    var startMillis = new Date().getTime();

    var datetimeToDecisB = timeB(0, 100);
    bindB($('#datetime'), mapB(function(d) { return d.toString(); }, datetimeToDecisB));

    var decisB = mapB(function(dt) { return Math.floor(dt.getTime() / 100); }, datetimeToDecisB);
    bindB($('#deciseconds'), decisB);

    var decisWrapE = filterE(function(value) { return value % 10 == 0; }, changesE(decisB));
    var decisWhenWrappedB = stepperB(decisB.value, snapshotE(decisB, decisWrapE));

    var secondsB = mapB(function(decis) { return Math.floor(decis / 10); }, decisB);
    var secondsB2 = mapB_2(function(decis) { return Math.floor(decis / 10); }, decisB);
    bindB($('#seconds'), secondsB);
    bindB($('#seconds2'), secondsB2);

    var secondsB3 = mapB(function(decis) { return Math.floor(decis / 10); }, decisWhenWrappedB);
    var isEven = function(n) { return n % 2 == 0; }
    var secondsEvenB = mapB(isEven, secondsB3)

    var stutterB = switcherB(decisB,
                             mapE(function(even) { return even ? decisB : decisWhenWrappedB; },
                                  changesE(secondsEvenB)));
    
    bindB($('#secondsEven'), mapB(function(even) { return even ? "YES \\(^_^)/" : "NO ;_;"; }, secondsEvenB));
    bindB($('#stutter'), stutterB);

    var template = ich['icanhaz-example'];
    bindB($('#icanhaz-output'), mapB(template,
                                     mapB(function(decis) { return {list: _.range(decis % 10)}; },
                                          decisB)));

That was fun! If you agree, I highly recommend the literature surrounding this. There are a number of additional primitives needed, and most libraries provide a huge pile of them. The closest thing I can recall seeing about figuring out canonical primitives is this rather challenging paper.

For real libraries, I think Reactive in Haskell is the state of the art. I’m not sure about libraries in other languages, but would love to hear about them.

});

Tagged , , ,

Type Classes in Scala

Type classes are an idea originally from Haskell that you can implement in Scala via implicit parameters. This is not new or obscure, but the technique is so useful that another post just demonstrating the basics can’t hurt. I will keep this brief — I have a lot to say about type classes, but first the fundamentals need to be extremely solid.

Let’s get imports out of the way.

import org.scalacheck._
import org.scalacheck.Prop._
import org.scalacheck.Gen._

Suppose I create a type such as a major-minor version scheme, or some such. It doesn’t really matter for this example.

case class Version(major: Int, minor: Int)

And suppose I want to serialize and deserialize it. To do this with a type class, I create a trait Serialize[T] that provides the ability to serialize/deserialize any value of type T. Implicit values implementing this trait are instances of the type class. (I prefix the methods with underscore to avoid name conflicts later, and this is not going to bother anyone because you should never call these methods directly. This is an entirely optional stylistic choice.)

trait Serialize[T] {
  def _serialize(v:T) : String
  def _deserialize(s:String) : Option[T] // May fail. In a robust implementation, we'd return Either[T, ReasonItFailed]
}

Next, I define functions that utilize this trait. These are the methods of the type class. A typical place for them is in the companion object. I also include a ScalaCheck property that should hold of any instance of the type class.

object Serialize {
  def serialize[T](v: T)(implicit instance: Serialize[T]) =
    instance._serialize(v)
  
  def deserialize[T](s: String)(implicit instance: Serialize[T]) =
    instance._deserialize(s)

  def deserializeSerialize[T](v: T)(implicit instance: Serialize[T]) : Prop
    = (deserialize(serialize(v)) ?= Some(v))
}

Now the type class is defined and I move on to instantiating it. In this case, I start by defining instances for standard types to build up to my own; this is common. Often instances for the standard types will also be shipped with the type class in the same companion object, but here they are in a totally separate module (if you are a seasoned OO programmer, this should already blow your mind a little). I’m not actually trying to make this robust, as you will generally want to go through an intermediate format such as JSON or XML which is more trivially compositional.

object SerializeInstances {
  import Serialize._

  implicit object serializeString extends Serialize[String] {
    def _serialize(v: String) = v
    def _deserialize(s: String) = Some(s)
  }

  implicit object serializeInt extends Serialize[Int] {
    def _serialize(v: Int) = v.toString
    def _deserialize(s: String) =
      try { Some(s.toInt) } catch { case _ => None }
  }

  implicit def serializeTuple2[A, B]
    (implicit l: Serialize[A], r: Serialize[B]) = new Serialize[(A, B)] {

      def escape(s: String) = s.replaceAll("&", "&amp;")
                               .replaceAll(",", "&comma;")

      def unescape(s: String) = s.replaceAll("&comma;", ",")
                                 .replaceAll("&amp;", "&")

      def _serialize(v: (A, B)) = 
        escape(serialize(v._1)) + "," + escape(serialize(v._2))

      def _deserialize(s: String) =
        s.split(",", -1).toList match {
          case leftS :: rightS :: Nil => {
            for(left <- deserialize[A](unescape(leftS));
                right <- deserialize[B](unescape(rightS)))
            yield (left, right)
          }
          case _ => None
        }
    }
}

Now I do a quick check (oblique pun intended) that the property holds via the sbt console.

$ xsbt console

scala> import Serialize._
import Serialize._

scala> import SerializeInstances._
import SerializeInstances._

scala> serialize(3)
res0: String = 3

scala> serialize("foo")
res1: String = foo

scala> serialize( (3, (5, "hello")) )
res2: String = 3,5&comma;hello

scala> serialize( ("foo", 3), (7, 22))
res3: String = foo&comma;3,7&comma;22

scala> import org.scalacheck.Prop._
import org.scalacheck.Prop._

scala> forAll(deserializeSerialize[String] _) check
+ OK, passed 100 tests.

scala> forAll(deserializeSerialize[Int] _) check
+ OK, passed 100 tests.

scala> forAll(deserializeSerialize[(Int, String)] _) check
+ OK, passed 100 tests.

scala> forAll(deserializeSerialize[((String, Int), (Int, (Int, Int)))] _).check
+ OK, passed 100 tests.

In a real project, you probably want to use ScalaTest to manage a suite of tests. I tend to mix in Spec with Checkers.

Coming back to the Version type, we make a Serialize instance for it. To test it, we also need to make an instance of Arbitrary – ScalaCheck is a key example of a great library built on type classes. Note how in a brief and simple line I define a fuzz test generator (org.scalacheck.Gen[Version]) and wrap it into an Arbitrary[Version] instance.

object Version {
  import Serialize._
  import SerializeInstances._

  implicit def arbVersion =
    Arbitrary[Version] { resultOf(Version.apply _) }

  implicit object serializeVersion extends Serialize[Version] {
    def _serialize(v: Version) = Serialize.serialize(v.major, v.minor)
    def _deserialize(s: String) = {
      for((major, minor) <- deserialize[(Int, Int)](s))
      yield Version(major, minor)
    }
  }
}

And check the property for this instance:

$ xsbt console

...

scala> import Version._
import Version._

scala> forAll(deserializeSerialize[Version] _) check
+ OK, passed 100 tests.

There are a couple of reasonable interpretations of the definition / instantiation of type classes:

  1. A type class is an interface that a static type may implement. The implicit parameter is the method table.
  2. A type class is a property that may hold of a static type. The implicit parameter is how we get the Scala compiler to infer that the type has this property and provide evidence.

Both of the above make sense with the alternate syntax that Scala allows for implicit parameters used in this style: “def foo[T](implicit instance: Serialize[T])” may be shortened to “def foo[T: Serialize]“. In the latter syntax, you cannot actually name and call the methods of the instance; it is simply makes it possible to call serialize and deserialize by bringing the type class instance into scope.

There are huge number of advantages to type classes over simply implementing a trait. Here are some easily tangible ones:

  1. The Version type did not have to know about the serialization or deserialization code. In many real-world scenarios the instance may actually be provided by a separate library. This is probably the number one reason to use type classes. They introduce a form of code decoupling and reuse that simply does not exist without them.
  2. The Scala compiler automatically derives serialization/deserialization for tuples. Inferring instances for lists, etc, is just as easy. In a type class based project, the compiler writes a ton of code for you.
  3. I was able to implement deserialize in the same type class as serialize. This cannot be done by implementing an interface as in “case class Version(...) extends Serializable” because the method is selected by the return type — it is obviously not an appropriate method of String!. In a more Java-esque world, you tend to have a separate — extraneous, in light of this — interface for a factory. For the logically inclined: Java-style OO has an unfortunate asymmetry between introduction and elimination forms.

As a side note, having the scalacheck property helps to avoid the black hole of defensive programming, where you carefully check for errors and unexpected results. Instead, you can enjoy what I like to call offensive programming (pun definitely intended) where you blithely assume your tests have covered all corner cases.

Should you like to see more examples and explanations:

  • An academic paper, perhaps the origination of the idea in Scala: Type Classes as Objects and Implicits by Oliviera, Moors, and Odersky.
  • ScalaCheck uses type classes heavily to make it easy to build test case generators.
  • Scalaz includes tons of type classes and instances, and much more.
  • json-scalaz uses JSONR / JSONW / JSON type classes, which I’m going to write about later.
  • scala-relaxng, authored by yours truly at Inkling, uses a pretty-printing typeclass to test the parser on arbitrary rnc schemas, somewhat like this post.
  • Countless blog posts. Search for them and enjoy!
Tagged , ,
Follow

Get every new post delivered to your Inbox.