Note: If you jumped to this page because you’re interested in applicative functors, you should definitely read Semigroupal and Apply first.
Functors, Applicative Functors and Monoids:
Meet the
Applicativetypeclass. It lies in theControl.Applicativemodule and it defines two methods,pureand<*>.
Let’s see Cats’ Applicative:
@typeclass trait Applicative[F[_]] extends Apply[F] { self =>
/**
* `pure` lifts any value into the Applicative Functor
*
* Applicative[Option].pure(10) = Some(10)
*/
def pure[A](x: A): F[A]
....
}
It’s an extension of Apply with pure.
LYAHFGG:
pureshould take a value of any type and return an applicative value with that value inside it. … A better way of thinking aboutpurewould be to say that it takes a value and puts it in some sort of default (or pure) context—a minimal context that still yields that value.
It seems like it’s basically a constructor that takes value A and returns F[A].
import cats._, cats.syntax.all._
Applicative[List].pure(1)
// res0: List[Int] = List(1)
Applicative[Option].pure(1)
// res1: Option[Int] = Some(value = 1)
This actually comes in handy using Apply[F].ap so we can avoid calling {{...}.some}.
{
val F = Applicative[Option]
F.ap({ F.pure((_: Int) + 3) })(F.pure(9))
}
// res2: Option[Int] = Some(value = 12)
We’ve abstracted Option away from the code.
LYAHFGG:
Let’s try implementing a function that takes a list of applicatives and returns an applicative that has a list as its result value. We’ll call it
sequenceA.
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
Let’s try implementing this with Cats!
def sequenceA[F[_]: Applicative, A](list: List[F[A]]): F[List[A]] = list match {
case Nil => Applicative[F].pure(Nil: List[A])
case x :: xs => (x, sequenceA(xs)) mapN {_ :: _}
}
Let’s test it:
sequenceA(List(1.some, 2.some))
// res3: Option[List[Int]] = Some(value = List(1, 2))
sequenceA(List(3.some, none[Int], 1.some))
// res4: Option[List[Int]] = None
sequenceA(List(List(1, 2, 3), List(4, 5, 6)))
// res5: List[List[Int]] = List(
// List(1, 4),
// List(1, 5),
// List(1, 6),
// List(2, 4),
// List(2, 5),
// List(2, 6),
// List(3, 4),
// List(3, 5),
// List(3, 6)
// )
We got the right answers. What’s interesting here is that we did end up needing
Applicative after all, and sequenceA is generic in a typeclassy way.
Using
sequenceAis useful when we have a list of functions and we want to feed the same input to all of them and then view the list of results.
For Function1 with Int fixed example, we need some type annotation:
{
val f = sequenceA[Function1[Int, *], Int](List((_: Int) + 3, (_: Int) + 2, (_: Int) + 1))
f(3)
}
// res6: List[Int] = List(6, 5, 4)
Here are the laws for Applicative:
pure id <*> v = v
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u
Cats defines another law
def applicativeMap[A, B](fa: F[A], f: A => B): IsEq[F[B]] =
fa.map(f) <-> fa.ap(F.pure(f))
This seem to say that if you combine F.ap and F.pure, you should get the same effect as F.map.
It took us a while, but I am glad we got this far. We’ll pick it up from here later.