Unapply 

One thing that I’ve been fighting the Scala compiler over is the lack of type inference support across the different kinded types like F[M[_, _]] and F[M[_]], and M[_] and F[M[_]].

For example, an instance of Applicative[M[_]] is (* -> *) -> * (a type constructor that takes another type constructor that that takes exactly one type). It’s known that Int => Int can be treated as an applicative by treating it as Int => A:

scala> Applicative[Function1[Int, Int]]
<console>:14: error: Int => Int takes no type parameters, expected: one
              Applicative[Function1[Int, Int]]
                          ^

scala> Applicative[({type l[A]=Function1[Int, A]})#l]
res14: scalaz.Applicative[[A]Int => A] = scalaz.std.FunctionInstances$$anon$2@56ae78ac

This becomes annoying for M[_,_] like Validation. One of the way Scalaz helps you out is to provide meta-instances of typeclass instance called Unapply.

trait Unapply[TC[_[_]], MA] {
  /** The type constructor */
  type M[_]
  /** The type that `M` was applied to */
  type A
  /** The instance of the type class */
  def TC: TC[M]
  /** Evidence that MA =:= M[A] */
  def apply(ma: MA): M[A]
}

When Scalaz method like traverse requires you to pass in Applicative[M[_]], it instead could ask for Unapply[Applicative, X]. During compile time, Scalac can look through all the implicit converters to see if it can coerce Function1[Int, Int] into M[A] by fixing or adding a parameter and of course using an existing typeclass instance.

scala> implicitly[Unapply[Applicative, Function1[Int, Int]]]
res15: scalaz.Unapply[scalaz.Applicative,Int => Int] = scalaz.Unapply_0$$anon$9@2e86566f

The feature I added yesterday allows type A to be promoted as M[A] by adding a fake type constructor. This let us treat Int as Applicative easier. But because it still requires TC0: TC[({type λ[α] = A0})#λ] implicitly, it does not allow just any type to be promoted as Applicative.

scala> implicitly[Unapply[Applicative, Int]]
res0: scalaz.Unapply[scalaz.Applicative,Int] = scalaz.Unapply_3$$anon$1@5179dc20

scala> implicitly[Unapply[Applicative, Any]]
<console>:14: error: Unable to unapply type `Any` into a type constructor of kind `M[_]` that is classified by the type class `scalaz.Applicative`
1) Check that the type class is defined by compiling `implicitly[scalaz.Applicative[<type constructor>]]`.
2) Review the implicits in object Unapply, which only cover common type 'shapes'
(implicit not found: scalaz.Unapply[scalaz.Applicative, Any])
              implicitly[Unapply[Applicative, Any]]
                        ^

Works. The upshot of all this is that we can now rewrite the following a bit cleaner:

scala> val failedTree: Tree[Validation[String, Int]] = 1.success[String].node(
         2.success[String].leaf, "boom".failure[Int].leaf)
failedTree: scalaz.Tree[scalaz.Validation[String,Int]] = <tree>

scala> failedTree.sequence[({type l[X]=Validation[String, X]})#l, Int]
res2: scalaz.Validation[java.lang.String,scalaz.Tree[Int]] = Failure(boom)

Here’s sequenceU:

scala> failedTree.sequenceU
res3: scalaz.Validation[String,scalaz.Tree[Int]] = Failure(boom)

Boom.