Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp Functor, Applicative, Traverse doc #1513

Merged
merged 1 commit into from
Jan 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 12 additions & 15 deletions docs/src/main/resources/microsite/data/menu.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
options:

#############################
#############################
# Type Classes Menu Options #
#############################

Expand All @@ -13,7 +13,7 @@ options:
url: typeclasses/semigroup.html
menu_type: typeclasses
menu_section: semigroupsandmonoids

nested_options:
- title: Semigroup
url: typeclasses/semigroup.html
Expand All @@ -23,30 +23,27 @@ options:
menu_section: semigroupsandmonoids

- title: Applicative and Traversable Functors
url: typeclasses/functor.html
url: typeclasses/applicativetraverse.html
menu_type: typeclasses
menu_section: aplicative
menu_section: applicative

nested_options:
- title: Functor
url: typeclasses/functor.html
menu_section: aplicative
- title: Apply
url: typeclasses/apply.html
menu_section: aplicative
menu_section: applicative
- title: Applicative
url: typeclasses/applicative.html
menu_section: aplicative
menu_section: applicative
- title: Traverse
url: typeclasses/traverse.html
menu_type: typeclasses
menu_section: aplicative
menu_section: applicative

- title: Monads
url: typeclasses/functor.html
menu_type: typeclasses
menu_section: monads

nested_options:
- title: Functor
url: typeclasses/functor.html
Expand All @@ -66,7 +63,7 @@ options:
url: typeclasses/functor.html
menu_type: typeclasses
menu_section: variance

nested_options:
- title: Functor
url: typeclasses/functor.html
Expand Down Expand Up @@ -102,7 +99,7 @@ options:
url: typeclasses/show.html
menu_type: typeclasses

###########################
###########################
# Data Types Menu Options #
###########################

Expand Down Expand Up @@ -144,4 +141,4 @@ options:

- title: Validated
url: datatypes/validated.html
menu_type: data
menu_type: data
286 changes: 259 additions & 27 deletions docs/src/main/tut/typeclasses/applicative.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,277 @@ source: "core/src/main/scala/cats/Applicative.scala"
scaladoc: "#cats.Applicative"
---
# Applicative
`Applicative` extends [`Functor`](functor.html) with an `ap` and `pure` method.

`Applicative` extends [`Apply`](apply.html) by adding a single method,
`pure`:
```tut:book:silent
import cats.Functor

```scala
def pure[A](x: A): F[A]
````
trait Applicative[F[_]] extends Functor[F] {
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]

def pure[A](a: A): F[A]

def map[A, B](fa: F[A])(f: A => B): F[B] = ap(pure(f))(fa)
}
```

`pure` wraps the value into the type constructor - for `Option` this could be `Some(_)`, for `Future`
`Future.successful`, and for `List` a singleton list.

`ap` is a bit tricky to explain and motivate, so we'll look at an alternative but equivalent
formulation via `product`.

```tut:book:silent
trait Applicative[F[_]] extends Functor[F] {
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]

def pure[A](a: A): F[A]
}

// Example implementation for right-biased Either
implicit def applicativeForEither[L]: Applicative[Either[L, ?]] = new Applicative[Either[L, ?]] {
def product[A, B](fa: Either[L, A], fb: Either[L, B]): Either[L, (A, B)] = (fa, fb) match {
case (Right(a), Right(b)) => Right((a, b))
case (Left(l) , _ ) => Left(l)
case (_ , Left(l) ) => Left(l)
}

def pure[A](a: A): Either[L, A] = Right(a)

def map[A, B](fa: Either[L, A])(f: A => B): Either[L, B] = fa match {
case Right(a) => Right(f(a))
case Left(l) => Left(l)
}
}
```

Note that in this formulation `map` is left abstract, whereas in the previous one with `ap` `map`
could be implemented in terms of `ap` and `pure`. This suggests that `ap` is equivalent to
`map` and `product`, which is indeed the case.

Such an `Applicative` must obey three laws:

* Associativity: No matter the order in which you product together three values, the result is isomorphic
* `fa.product(fb).product(fc) ~ fa.product(fb.product(fc))`
* With `map`, this can be made into an equality with `fa.product(fb).product(fc) = fa.product(fb.product(fc)).map { case (a, (b, c)) => ((a, b), c) }`
* Left identity: Zipping a value on the left with unit results in something isomorphic to the original value
* `pure(()).product(fa) ~ fa`
* As an equality: `pure(()).product(fa).map(_._2) = fa`
* Right identity: Zipping a value on the right with unit results in something isomorphic to the original value
* `fa.product(pure(())) ~ fa`
* As an equality: `fa.product(pure(())).map(_._1) = fa`

## Applicatives for effect management

If we view `Functor` as the ability to work with a single effect, `Applicative` encodes working with
multiple **independent** effects. Between `product` and `map`, we can take two separate effectful values
and compose them. From there we can generalize to working with any N number of independent effects.

```tut:reset:book:silent
import cats.Applicative

def product3[F[_]: Applicative, A, B, C](fa: F[A], fb: F[B], fc: F[C]): F[(A, B, C)] = {
val F = Applicative[F]
val fabc = F.product(F.product(fa, fb), fc)
F.map(fabc) { case ((a, b), c) => (a, b, c) }
}
```

## What is ap?

Let's see what happens if we try to compose two effectful values with just `map`.

```tut:book:silent
import cats.instances.option._

val f: (Int, Char) => Double = (i, c) => (i + c).toDouble

val int: Option[Int] = Some(5)
val char: Option[Char] = Some('a')
```

```tut:book
int.map(i => (c: Char) => f(i, c)) // what now?
```

We have an `Option[Char => Double]` and an `Option[Double]` to which we want to apply the function to,
but `map` doesn't give us enough power to do that. Hence, `ap`.

## Applicatives compose

Like [`Functor`](functor.html), `Applicative`s compose. If `F` and `G` have `Applicative` instances, then so
does `F[G[_]]`.

```tut:book:silent
import cats.data.Nested
import cats.instances.future._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val x: Future[Option[Int]] = Future.successful(Some(5))
val y: Future[Option[Char]] = Future.successful(Some('a'))
```

```tut:book
val composed = Applicative[Future].compose[Option].map2(x, y)(_ + _)

val nested = Applicative[Nested[Future, Option, ?]].map2(Nested(x), Nested(y))(_ + _)
```

## Traverse

The straightforward way to use `product` and `map` (or just `ap`) is to compose `n` independent effects,
where `n` is a fixed number. In fact there are convenience methods named `apN`, `mapN`, and `tupleN` (replacing
`N` with a number 2 - 22) to make it even easier.

Imagine we have one `Option` representing a username, one representing a password, and another representing
a URL for logging into a database.

```tut:book:silent
import java.sql.Connection

val username: Option[String] = Some("username")
val password: Option[String] = Some("password")
val url: Option[String] = Some("some.login.url.here")

// Stub for demonstration purposes
def attemptConnect(username: String, password: String, url: String): Option[Connection] = None
```

This method takes any value and returns the value in the context of
the functor. For many familiar functors, how to do this is
obvious. For `Option`, the `pure` operation wraps the value in
`Some`. For `List`, the `pure` operation returns a single element
`List`:
We know statically we have 3 `Option`s, so we can use `map3` specifically.

```tut:book
import cats._
Applicative[Option].map3(username, password, url)(attemptConnect)
```

Sometimes we don't know how many effects will be in play - perhaps we are receiving a list from user
input or getting rows from a database. This implies the need for a function:

```tut:book:silent
def sequenceOption[A](fa: List[Option[A]]): Option[List[A]] = ???

// Alternatively..
def traverseOption[A, B](as: List[A])(f: A => Option[B]): Option[List[B]] = ???
```

Users of the standard library `Future.sequence` or `Future.traverse` will find these names and signatures
familiar.

Let's implement `traverseOption` (you can implement `sequenceOption` in terms of `traverseOption`).

```tut:book:silent
def traverseOption[A, B](as: List[A])(f: A => Option[B]): Option[List[B]] =
as.foldRight(Some(List.empty[B]): Option[List[B]]) { (a: A, acc: Option[List[B]]) =>
val optB: Option[B] = f(a)
// optB and acc are independent effects so we can use Applicative to compose
Applicative[Option].map2(optB, acc)(_ :: _)
}
```

```tut:book
traverseOption(List(1, 2, 3))(i => Some(i): Option[Int])
```

This works...but if we look carefully at the implementation there's nothing `Option`-specific going on. As
another example let's implement the same function but for `Either`.

```tut:book:silent
import cats.instances.either._

def traverseEither[E, A, B](as: List[A])(f: A => Either[E, B]): Either[E, List[B]] =
as.foldRight(Right(List.empty[B]): Either[E, List[B]]) { (a: A, acc: Either[E, List[B]]) =>
val eitherB: Either[E, B] = f(a)
Applicative[Either[E, ?]].map2(eitherB, acc)(_ :: _)
}
```

```tut:book
traverseEither(List(1, 2, 3))(i => if (i % 2 != 0) Left(s"${i} is not even") else Right(i / 2))
```

The implementation of `traverseOption` and `traverseEither` are more or less identical, modulo the initial
"accumulator" to `foldRight`. But even that could be made the same by delegating to `Applicative#pure`!
Generalizing `Option` and `Either` to any `F[_]: Applicative` gives us the fully polymorphic version.
Existing data types with `Applicative` instances (`Future`, `Option`, `Either[E, ?]`, `Try`) can call it by fixing `F`
appropriately, and new data types need only be concerned with implementing `Applicative` to do so as well.

```tut:book:silent
def traverse[F[_]: Applicative, A, B](as: List[A])(f: A => F[B]): F[List[B]] =
as.foldRight(Applicative[F].pure(List.empty[B])) { (a: A, acc: F[List[B]]) =>
val fb: F[B] = f(a)
Applicative[F].map2(fb, acc)(_ :: _)
}
```

This function is provided by Cats via the `Traverse[List]` instance and syntax, which is covered in another
tutorial.

```tut:book:silent
import cats.instances.list._
import cats.syntax.traverse._
```

```tut:book
List(1, 2, 3).traverse(i => Some(i): Option[Int])
```

With this addition of `traverse`, we can now compose any number of independent effects, statically known or otherwise.

## Apply - a weakened Applicative
A closely related type class is `Apply` which is identical to `Applicative`, modulo the `pure`
method. Indeed in Cats `Applicative` is a subclass of `Apply` with the addition of this method.

```scala
trait Apply[F[_]] extends Functor[F] {
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
}

trait Applicative[F[_]] extends Apply[F] {
def pure[A](a: A): F[A]

def map[A, B](fa: F[A])(f: A => B): F[B] = ap(pure(f))(fa)
}
```

The laws for `Apply` are just the laws of `Applicative` that don't mention `pure`. In the laws given
above, the only law would be associativity.

One of the motivations for `Apply`'s existence is that some types have `Apply` instances but not
`Applicative` - one example is `Map[K, ?]`. Consider the behavior of `pure` for `Map[K, A]`. Given
a value of type `A`, we need to associate some arbitrary `K` to it but we have no way of doing that.

However, given existing `Map[K, A]` and `Map[K, B]` (or `Map[K, A => B]`), it is straightforward to
pair up (or apply functions to) values with the same key. Hence `Map[K, ?]` has an `Apply` instance.

## Syntax

Syntax for `Applicative` (or `Apply`) is available under the `cats.implicits._` import. The most
interesting syntax is focused on composing independent effects - there are two options for this.

The first is the builder syntax which incrementally builds up a collection of effects until a
function is applied to compose them.

```tut:book:silent
import cats.implicits._

Applicative[Option].pure(1)
Applicative[List].pure(1)
val o1: Option[Int] = Some(42)
val o2: Option[String] = Some("hello")
```

Like [`Functor`](functor.html) and [`Apply`](apply.html), `Applicative`
functors also compose naturally with each other. When
you compose one `Applicative` with another, the resulting `pure`
operation will lift the passed value into one context, and the result
into the other context:
```tut:book
(o1 |@| o2).map((i: Int, s: String) => i.toString ++ s)
(o1 |@| o2).tupled
```

The second expects the effects in a tuple and works by enriching syntax on top of the existing
`TupleN` types.

```tut:book
import cats.data.Nested
val nested = Applicative[Nested[List, Option, ?]].pure(1)
val unwrapped = nested.value
(o1, o2).map2((i: Int, s: String) => i.toString ++ s)
```

## Applicative Functors & Monads
## Further Reading

`Applicative` is a generalization of [`Monad`](monad.html), allowing expression
of effectful computations in a pure functional way.
* [Applicative Programming with Effects][applicativePaper] - McBride, Patterson. JFP 2008.

`Applicative` is generally preferred to `Monad` when the structure of a
computation is fixed a priori. That makes it possible to perform certain
kinds of static analysis on applicative values.
[applicativePaper]: http://www.staff.city.ac.uk/~ross/papers/Applicative.html "Applicative Programming with Effects"
Loading