I have a bit of a sense of the Aux pattern (as used in shapeless and elsewhere) in which a type member is extracted into a type parameter, and I know it\'s a workaround the
Simply speaking this pattern lets you establish a relation between two generic type parameters.
Let's take a look at shapeless' LabelledGeneric
type class which gives you a generic HList
representation for case classes:
trait LabelledGeneric[T] {
type Repr
}
T
is the input type, i.e LabelledGeneric[MyCaseClass]
will give you the HList representation of MyCaseClass
. Repr
is the output type, i.e. the HList type corresponding to T
.
Let's write a method that takes a Generic
instance and needs another parameter of the output type. For instance we could use Keys
to collect the field names of a labelled generic
def fieldNames[T](implicit gen: LabelledGeneric[T], keys: Keys[gen.Repr]): keys.Repr …
Except that this doesn't work because Scala doesn't let you access gen
or keys
here. We can either have a concrete type or a type variable.
And that's where Aux comes into play: It let's us "lift" gen.Repr
into a type variable:
object Generic {
type Aux[T, Repr0] = Generic[T] { type Repr = Repr0 }
}
As you can see the Aux
type gives us a way from Repr
to a type variable, so we can finally define foo
:
def foo[T, Repr, K](
implicit gen: LabelledGeneric.Aux[T, Repr],
keys: Keys.Aux[Repr, K]
): K …
If you are familiar with Prolog you can read Aux as a predicate that proves a relation between two type variables. In the above example you can read it as "LabelledGeneric proves that Repr
is the generic representation with labels of T, and Keys.Aux proves that K is a list of all keys of Repr".