Cartesian 

Functors, Applicative Functors and Monoids:

So far, when we were mapping functions over functors, we usually mapped functions that take only one parameter. But what happens when we map a function like *, which takes two parameters, over a functor?

scala> import cats._, cats.instances.all._
import cats._
import cats.instances.all._

scala> val hs = Functor[List].map(List(1, 2, 3, 4)) ({(_: Int) * (_:Int)}.curried)
hs: List[Int => Int] = List(<function1>, <function1>, <function1>, <function1>)

scala> Functor[List].map(hs) {_(9)}
res1: List[Int] = List(9, 18, 27, 36)

LYAHFGG:

But what if we have a functor value of Just (3 *) and a functor value of Just 5, and we want to take out the function from Just(3 *) and map it over Just 5?

Meet the Applicative typeclass. It lies in the Control.Applicative module and it defines two methods, pure and <*>.

Cats splits this into Cartesian, Apply, and Applicative. Here’s the contract for Cartesian:

/**
 * [[Cartesian]] captures the idea of composing independent effectful values.
 * It is of particular interest when taken together with [[Functor]] - where [[Functor]]
 * captures the idea of applying a unary pure function to an effectful value,
 * calling `product` with `map` allows one to apply a function of arbitrary arity to multiple
 * independent effectful values.
 *
 * That same idea is also manifested in the form of [[Apply]], and indeed [[Apply]] extends both
 * [[Cartesian]] and [[Functor]] to illustrate this.
 */
@typeclass trait Cartesian[F[_]] {
  def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}

Cartesian defines product function, which produces a pair of (A, B) wrapped in effect F[_] out of F[A] and F[B]. The symbolic alias for product is |@| also known as the applicative style.

Catnip 

Before we move on, let’s port Scalaz’s DSL to create Option values typed to Option.

scala> :paste
// Entering paste mode (ctrl-D to finish)
object Catnip {
  implicit class IdOp[A](val a: A) extends AnyVal {
    def some: Option[A] = Some(a)
  }
  def none[A]: Option[A] = None
}
import Catnip._

// Exiting paste mode, now interpreting.

defined object Catnip
import Catnip._

This allows us to shorten (Some(9): Option[Int]) to 9.some.

scala> 9.some
res2: Option[Int] = Some(9)

scala> none[Int]
res3: Option[Int] = None

The Applicative Style 

LYAHFGG:

With the Applicative type class, we can chain the use of the <*> function, thus enabling us to seamlessly operate on several applicative values instead of just one.

Here’s an example in Haskell:

ghci> pure (-) <*> Just 3 <*> Just 5
Just (-2)

Cats comes with the CartesianBuilder syntax.

scala> import cats.syntax.cartesian._
import cats.syntax.cartesian._

scala> (3.some |@| 5.some) map { _ - _ }
res4: Option[Int] = Some(-2)

scala> (none[Int] |@| 5.some) map { _ - _ }
res5: Option[Int] = None

scala> (3.some |@| none[Int]) map { _ - _ }
res6: Option[Int] = None

This shows that Option forms Cartesian.

List as a Cartesian 

LYAHFGG:

Lists (actually the list type constructor, []) are applicative functors. What a surprise!

Let’s see if we can use the CartesianBuilder sytax:

scala> (List("ha", "heh", "hmm") |@| List("?", "!", ".")) map {_ + _}
res7: List[String] = List(ha?, ha!, ha., heh?, heh!, heh., hmm?, hmm!, hmm.)

> and < operators 

Cartesian enables two operators, <* and *>, which are special cases of Apply[F].product:

abstract class CartesianOps[F[_], A] extends Cartesian.Ops[F, A] {
  def |@|[B](fb: F[B]): CartesianBuilder[F]#CartesianBuilder2[A, B] =
    new CartesianBuilder[F] |@| self |@| fb

  def *>[B](fb: F[B])(implicit F: Functor[F]): F[B] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => b }

  def <*[B](fb: F[B])(implicit F: Functor[F]): F[A] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => a }
}

The definition looks simple enough, but the effect is cool:

scala> 1.some <* 2.some
res8: Option[Int] = Some(1)

scala> none[Int] <* 2.some
res9: Option[Int] = None

scala> 1.some *> 2.some
res10: Option[Int] = Some(2)

scala> none[Int] *> 2.some
res11: Option[Int] = None

If either side fails, we get None.

Cartesian law 

Cartesian has a single law called associativity:

trait CartesianLaws[F[_]] {
  implicit def F: Cartesian[F]

  def cartesianAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): (F[(A, (B, C))], F[((A, B), C)]) =
    (F.product(fa, F.product(fb, fc)), F.product(F.product(fa, fb), fc))
}