The Aux Pattern

A short introduction to the `Aux` pattern

Posted by Luigi on September 13, 2015

Let’s start saying that the Aux pattern is not a pattern, is a technique used in every library that is doing some type level programming that we need to use to overcome one Scala limitation.

Every time we do a type level computation in Scala we have a type alias defined inside another class/trait, let’s see an example

trait Foo[A] {
  type B
  def value: B
}

In this case for instance the result of our type level computation will be stored in B.

So let’s define some instances:

implicit def fi = new Foo[Int] {
  type B = String
  val value = "Foo"
}
implicit def fs = new Foo[String] {
  type B = Boolean
  val value = false
}

Well, in this case we are not doing any real computation, we are just changing the type of B depending on the input type A , that’s enough to understand Aux.

Now in Scala we can use parameter dependent types to access the type defined inside a class/trait (path dependent type) so if we want to use our type B in a function, as a return type, we can do that:

  def foo[T](t: T)(implicit f: Foo[T]): f.B = f.value
  val res1: String = foo(2)
  val res2: Boolean = foo("")

In this example we see that we can change the return type of a function using dependent type and the implicit resolution, now let’s suppose that we want to use this type as type parameter in the next parameter, for instance to get the Monoid instance for that type:

  import scalaz._, Scalaz._

  def foo[T](t: T)
            (implicit f: Foo[T], m: Monoid[f.B]): f.B = m.zero

We would like to do that, but unfortunately we get

illegal dependent method type: parameter appears in the type of another parameter in the same section or an earlier one

Scala tells us that we can’t use the dependent type in the same section, we can use it in the next parameters block or as a return type only.

Here is where our friend Aux is going to help, let’s define it:

  type Aux[A0, B0] = Foo[A0] { type B = B0  }

What we are doing here is defining a type alias where A0 is mapped to Foo A and B0 is mapped to type B, what I didn’t understand at the beginning is that the relation type B = B0 works both ways, so if we fix the type for B like with type B = Boolean, B0 will get this type too.

So now we can write this:

  def foo[T, R](t: T)(implicit f: Foo.Aux[T, R], m: Monoid[R]): R = m.zero 
  val res1: String = foo(2)
  val res2: Boolean = foo("")

The full example is here Gist

That’s it, basically Aux is just a way to extract the result of a type level computation, now let’s have a look at a real world example, the most common example that comes to my mind is shapeless Generic,

def length[T, R <: HList](t: T)
                         (implicit g: Generic.Aux[T, R],
                                   l: Length[R]): l.Out = l()

case class Foo(i: Int, s: String, b: Boolean)
val foo = Foo(1, "", false)

val res = length(foo)
println(s"res: ${Nat.toInt(res)}")

// res: 3

In this case Generic.Aux will extract the generic representation of T inside R and in this way we can use it to resolve the Length type class that we use to get the length of the Hlist.

Conclusion

Aux is a very simple technique that is mandatory when you start doing some type level programming, probably is that simple that nobody needed to write a tutorial about it so far, but because it took to me some effort to understand it, maybe that can help somebody.