Unboxed new types within Scalaz7

Some time ago I started investigating the latest (and as yet unreleased) version of Scalaz. Version 7.x is markedly different to version 6.x; utilising a totally different design that makes a distinct split between core abstractions and the syntax to work with said abstractions. In any case, thats fodder for another post; the bottom line is that Scalaz7 is really, really slick - I like the look of it a lot and I feel like theres a lot to be learnt by simply studying the codebase and throwing around the abstractions therein (this may be less true for haskell gurus, but for mere mortals like myself I’ve certainly found it insightful).

One of the really neat things that Scalaz7 makes use of is a very clever trick with types in order to disambiguate typeclass instances for a given type T. This was something Miles demonstrated some time ago in a gist, and I was intrigued to find it being used in Scalaz7.

So then, what the hell does all this mean you might be wondering? Well, consider a Monoid of type Int like so:

scala>import scalaz._, std.anyVal._
import scalaz._
import std.anyVal._

scala> Monoid[Int].append(10,20)
res1: Int = 30

…or with sugary syntax imported as well:

scala>import scalaz._, syntax.semigroup._, std.anyVal._
import scalaz._
import syntax.semigroup._
import std.anyVal._

scala> 10 |+| 20
res2: Int = 30

This is simple use of the Monoid type, and this example uses the default type class instance for Int, which simply sums two numbers together. But, consider another case for Int: what if you needed to multiply those same numbers instead?

If there were two typeclass instances for Monoid[Int], unless they were in separate objects, packages or jars there would be an implicit ambiguity which would not compile: the type system would not implciitly be able to determine which typeclass it should apply when considering an operation on monoids of Int.

This issue has been overcome in Scalaz7 by using the type tagging trick mentioned in the introduction. Specifically, the default behaviour for Monoid[Int] is addition, and then a second typeclass exists for Monoid[Int @@ Multiplication]. The @@ syntax probably looks a little funny, so lets look at how its used and then talk in more detail about how that works:

scala>import Tags._
import Tags._

scala> Multiplication(2) |+| Multiplication(10)
res14: scalaz.package.@@[Int,scalaz.Tags.Multiplication] = 20

You’ll notice that the value was multiplied this time, giving the correct result of 20, but you may well be wondering about the resulting type signature of @@[Int,Multiplication]… this is where it gets interesting.

Multiplication just acts as a small bit of plumbing to produce a type of A, tagged with Multiplication; and this gives you the ability to define a type which is distinct from another - even if they are “the same” (so to speak). The definition of Multiplication is like so:

sealed trait Multiplication
def Multiplication[A](a: A): A @@ Multiplication = Tag[A, Multiplication](a)

object Tag {
  @inline def apply[A, T](a: A): A @@ T = a.asInstanceOf[A @@ T]
  ...
}

The Tag object simply tags types of A with a specified marker of T; or, in this case, Multiplication, where the result is A @@ T. I grant you, it looks weird to start with, but the definition of @@ is quite straight forward:

type @@[T, Tag] = T with Tagged[Tag]

Where Tagged in turn is a structural type:

type Tagged[T] = {type Tag = T}

The really clever thing here is that this whole setup is just at the type-level: the values are just Int in the underlying byte code. That is to say if we had something like:

def foobar(i: Int): Int @@ Multiplication = ... 

When compiled it would actually end up being:

def foobar(i: Int): Int = … 

Which is pretty awesome. This sort of strategy is obviously quite generic and there are a range of different tags within Scalaz7, including selecting first and last operands of a monoid, zipping with applicatives, conjunction and disjunctions etc. All very neat stuff!

comments powered by Disqus