This gist explains how Tagged Types are implemented in ScalaZ. Hope this helps someone.
Snippets taken from Tim Perret's blog on tagged types in Scalaz 7.
First, let's take a look at the type definition:
type @@[T, Tag] = T with Tagged[Tag]
type Tagged[T] = {type Tag = T}
Substituting the type alias Tagged[Tag]
with its actual type gives you:
type @@[T, Tag] = T with { type Tag = T }
The RHS value T with { type Tag = T }
might look a little peculiar. Scala supports structural types and that's exactly what { type Tag = T }
is. Check out this example from the Twitter scala course on Types:
scala> def foo(x: { def get: Int }) = 123 + x.get
foo: (x: AnyRef{def get: Int})Int
scala> foo(new { def get = 10 })
res0: Int = 133
In the example above, you can pass in any type as long as the type has a method get
that returns an Int
.
Going back to our original discussion, for the type alias
type @@[T, Tag] = T with { type Tag = T }
it's saying @@[T, Tag]
is a type alias for a type T
that mixes in the structural type { type Tag = T }
.
Now, let's take a look at a more concrete example:
sealed trait Points
trait PaintPoints extends Points
trait ThreePointers extends Points
def PointsInsideThePaint[A](a: A): A @@ PaintPoints =
Tag[A, PaintPoints](a)
def PointsBehindTheArc[A](a: A): A @@ ThreePointers =
Tag[A, ThreePointers](a)
def getAveragePaintPoints(a: Int @@ PaintPoints, games: Int): Double =
Tag.unwrap(a).toDouble / games.toDouble
val paintPoints = PointsInsideThePaint(12)
val threePointers = PointsBehindTheArc(33)
// works, types align. paintPoints is @@[Int, PaintPoints] which is whwat getAveragePaintPoints expects
getAveragePaintPoints(paintPoints, 1)
// wouldn't work - expects @@[Int, PaintPoints], not @@[Int, ThreePointers]
getAveragePaintPoints(threePointers, 3)