diff --git a/docs/src/main/resources/microsite/data/menu.yml b/docs/src/main/resources/microsite/data/menu.yml index d11b0149d0..fc8b470ba6 100644 --- a/docs/src/main/resources/microsite/data/menu.yml +++ b/docs/src/main/resources/microsite/data/menu.yml @@ -1,6 +1,6 @@ options: - ############################# + ############################# # Type Classes Menu Options # ############################# @@ -13,7 +13,7 @@ options: url: typeclasses/semigroup.html menu_type: typeclasses menu_section: semigroupsandmonoids - + nested_options: - title: Semigroup url: typeclasses/semigroup.html @@ -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 @@ -66,7 +63,7 @@ options: url: typeclasses/functor.html menu_type: typeclasses menu_section: variance - + nested_options: - title: Functor url: typeclasses/functor.html @@ -102,7 +99,7 @@ options: url: typeclasses/show.html menu_type: typeclasses - ########################### + ########################### # Data Types Menu Options # ########################### @@ -144,4 +141,4 @@ options: - title: Validated url: datatypes/validated.html - menu_type: data \ No newline at end of file + menu_type: data diff --git a/docs/src/main/tut/typeclasses/applicative.md b/docs/src/main/tut/typeclasses/applicative.md index 17b537028c..1b666db9b5 100644 --- a/docs/src/main/tut/typeclasses/applicative.md +++ b/docs/src/main/tut/typeclasses/applicative.md @@ -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" diff --git a/docs/src/main/tut/typeclasses/applicativetraverse.md b/docs/src/main/tut/typeclasses/applicativetraverse.md new file mode 100644 index 0000000000..db9f11df22 --- /dev/null +++ b/docs/src/main/tut/typeclasses/applicativetraverse.md @@ -0,0 +1,40 @@ +--- +layout: docs +title: "Applicative and Traversable Functors" +section: "typeclasses" +scaladoc: "#cats.Functor" +--- + +# Applicative and Traversable Functors + +## An example from the standard library + +One of the most useful functions when working with `scala.concurrent.Future` is `Future.traverse`, presented below +in a simplified form. + +```tut:book:silent +import scala.concurrent.{ExecutionContext, Future} + +def traverseFuture[A, B](as: List[A])(f: A => Future[B])(implicit ec: ExecutionContext): Future[List[B]] = + Future.traverse(as)(f) +``` + +`traverseFuture` takes a `List[A]` and for each `A` in the list applies the function `f` to it, gathering results +as it goes along. `f` is often referred to as an *effectful* function, where the `Future` effect is running the computation +concurrently, presumably on another thread. This effect is apparent in the result of the function, which has +gathered results inside `Future`. + +But what if the effect we wanted wasn't `Future`? What if instead of concurrency for our effect we wanted validation +(`Option`, `Either`, `Validated`) or `State` ? It turns out we can abstract out the commonalities between all these +data types and write a generic `traverse` function once and for all. We can even go further and abstract over data +types that can be traversed over such as `List`, `Vector`, and `Option`. + +In this series we will build up the machinery needed to generalize the standard library's `Future.traverse` into +its fully abstract and most reusable form. + +If you'd like to read the published literature on these ideas, some good starting points are +"[Applicative Programming with Effects][applicativeProgramming]" by McBride and Patterson, and +"[The Essence of the Iterator Pattern][iterator]" by Gibbons and Oliveira. + +[applicativeProgramming]: http://www.staff.city.ac.uk/~ross/papers/Applicative.html "Applicative Programming with Effects" +[iterator]: https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf "The Essence of the Iterator Pattern" diff --git a/docs/src/main/tut/typeclasses/apply.md b/docs/src/main/tut/typeclasses/apply.md deleted file mode 100644 index 9652411957..0000000000 --- a/docs/src/main/tut/typeclasses/apply.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -layout: docs -title: "Apply" -section: "typeclasses" -source: "core/src/main/scala/cats/Apply.scala" -scaladoc: "#cats.Apply" ---- -# Apply - -`Apply` extends the [`Functor`](functor.html) type class (which features the familiar `map` -function) with a new function `ap`. The `ap` function is similar to `map` -in that we are transforming a value in a context (a context being the `F` in `F[A]`; -a context can be `Option`, `List` or `Future` for example). -However, the difference between `ap` and `map` is that for `ap` the function that -takes care of the transformation is of type `F[A => B]`, whereas for `map` it is `A => B`: - -```tut:silent -import cats._ - -val intToString: Int => String = _.toString -val double: Int => Int = _ * 2 -val addTwo: Int => Int = _ + 2 - -implicit val optionApply: Apply[Option] = new Apply[Option] { - def ap[A, B](f: Option[A => B])(fa: Option[A]): Option[B] = - fa.flatMap (a => f.map (ff => ff(a))) - - def map[A,B](fa: Option[A])(f: A => B): Option[B] = fa map f -} - -implicit val listApply: Apply[List] = new Apply[List] { - def ap[A, B](f: List[A => B])(fa: List[A]): List[B] = - fa.flatMap (a => f.map (ff => ff(a))) - - def map[A,B](fa: List[A])(f: A => B): List[B] = fa map f -} -``` - -### map - -Since `Apply` extends `Functor`, we can use the `map` method from `Functor`: - -```tut:book -Apply[Option].map(Some(1))(intToString) -Apply[Option].map(Some(1))(double) -Apply[Option].map(None)(double) -``` - -### compose - -And like functors, `Apply` instances also compose (via the `Nested` data type): - -```tut:book -import cats.data.Nested -val listOpt = Nested[List, Option, Int](List(Some(1), None, Some(3))) -val plusOne = (x:Int) => x + 1 -val f = Nested[List, Option, Int => Int](List(Some(plusOne))) -Apply[Nested[List, Option, ?]].ap(f)(listOpt) -``` - -### ap -The `ap` method is a method that `Functor` does not have: - -```tut:book -Apply[Option].ap(Some(intToString))(Some(1)) -Apply[Option].ap(Some(double))(Some(1)) -Apply[Option].ap(Some(double))(None) -Apply[Option].ap(None)(Some(1)) -Apply[Option].ap(None)(None) -``` - -### ap2, ap3, etc - -`Apply` also offers variants of `ap`. The functions `apN` (for `N` between `2` and `22`) -accept `N` arguments where `ap` accepts `1`: - -For example: - -```tut:book -val addArity2 = (a: Int, b: Int) => a + b -Apply[Option].ap2(Some(addArity2))(Some(1), Some(2)) - -val addArity3 = (a: Int, b: Int, c: Int) => a + b + c -Apply[Option].ap3(Some(addArity3))(Some(1), Some(2), Some(3)) -``` - -Note that if any of the arguments of this example is `None`, the -final result is `None` as well. The effects of the context we are operating on -are carried through the entire computation: - -```tut:book -Apply[Option].ap2(Some(addArity2))(Some(1), None) -Apply[Option].ap4(None)(Some(1), Some(2), Some(3), Some(4)) -``` - -### map2, map3, etc - -Similarly, `mapN` functions are available: - -```tut:book -Apply[Option].map2(Some(1), Some(2))(addArity2) - -Apply[Option].map3(Some(1), Some(2), Some(3))(addArity3) -``` - -### tuple2, tuple3, etc - -And `tupleN`: - -```tut:book -Apply[Option].tuple2(Some(1), Some(2)) - -Apply[Option].tuple3(Some(1), Some(2), Some(3)) -``` - -## apply builder syntax - -The `|@|` operator offers an alternative syntax for the higher-arity `Apply` -functions (`apN`, `mapN` and `tupleN`). -In order to use it, first import `cats.syntax.all._` or `cats.syntax.cartesian._`. -Here we see that the following two functions, `f1` and `f2`, are equivalent: - -```tut:book -import cats.implicits._ - -def f1(a: Option[Int], b: Option[Int], c: Option[Int]) = - (a |@| b |@| c) map { _ * _ * _ } -def f2(a: Option[Int], b: Option[Int], c: Option[Int]) = - Apply[Option].map3(a, b, c)(_ * _ * _) - -f1(Some(1), Some(2), Some(3)) - -f2(Some(1), Some(2), Some(3)) -``` - -All instances created by `|@|` have `map`, `ap`, and `tupled` methods of the appropriate arity: - -```tut:book -val option2 = Option(1) |@| Option(2) -val option3 = option2 |@| Option.empty[Int] - -option2 map addArity2 -option3 map addArity3 - -option2 apWith Some(addArity2) -option3 apWith Some(addArity3) - -option2.tupled -option3.tupled -``` diff --git a/docs/src/main/tut/typeclasses/functor.md b/docs/src/main/tut/typeclasses/functor.md index 7ca0489c9d..527a28c843 100644 --- a/docs/src/main/tut/typeclasses/functor.md +++ b/docs/src/main/tut/typeclasses/functor.md @@ -6,137 +6,107 @@ source: "core/src/main/scala/cats/Functor.scala" scaladoc: "#cats.Functor" --- # Functor +`Functor` is a type class that abstracts over type constructors that can be `map`'ed over. Examples of such +type constructors are `List`, `Option`, and `Future`. -A `Functor` is a ubiquitous type class involving types that have one -"hole", i.e. types which have the shape `F[?]`, such as `Option`, -`List` and `Future`. (This is in contrast to a type like `Int` which has -no hole, or `Tuple2` which has two holes (`Tuple2[?,?]`)). - -The `Functor` category involves a single operation, named `map`: +```tut:book:silent +trait Functor[F[_]] { + def map[A, B](fa: F[A])(f: A => B): F[B] +} -```scala -def map[A, B](fa: F[A])(f: A => B): F[B] +// Example implementation for Option +implicit val functorForOption: Functor[Option] = new Functor[Option] { + def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match { + case None => None + case Some(a) => Some(f(a)) + } +} ``` -This method takes a function `A => B` and turns an `F[A]` into an -`F[B]`. The name of the method `map` should remind you of the `map` -method that exists on many classes in the Scala standard library, for -example: +A `Functor` instance must obey two laws: -```tut:book -Option(1).map(_ + 1) -List(1,2,3).map(_ + 1) -Vector(1,2,3).map(_.toString) -``` +* Composition: Mapping with `f` and then again with `g` is the same as mapping once with the composition of `f` and `g` + * `fa.map(f).map(g) = fa.map(f.andThen(g))` -## Creating Functor instances +* Identity: Mapping with the identity function is a no-op + * `fa.map(x => x) = fa` -We can trivially create a `Functor` instance for a type which has a well -behaved `map` method: +## A different view -```tut:silent -import cats._ +Another way of viewing a `Functor[F]` is that `F` allows the lifting of a pure function `A => B` into the effectful +function `F[A] => F[B]`. We can see this if we re-order the `map` signature above. -implicit val optionFunctor: Functor[Option] = new Functor[Option] { - def map[A,B](fa: Option[A])(f: A => B) = fa map f -} +```tut:book:silent +trait Functor[F[_]] { + def map[A, B](fa: F[A])(f: A => B): F[B] -implicit val listFunctor: Functor[List] = new Functor[List] { - def map[A,B](fa: List[A])(f: A => B) = fa map f + def lift[A, B](f: A => B): F[A] => F[B] = + fa => map(fa)(f) } ``` -However, functors can also be created for types which don't have a `map` -method. For example, if we create a `Functor` for `Function1[In, ?]` -we can use `andThen` to implement `map`: +## Functors for effect management -```tut:silent -implicit def function1Functor[In]: Functor[Function1[In, ?]] = - new Functor[Function1[In, ?]] { - def map[A,B](fa: In => A)(f: A => B): Function1[In,B] = fa andThen f - } -``` +The `F` in `Functor` is often referred to as an "effect" or "computational context." Different effects will +abstract away different behaviors with respect to fundamental functions like `map`. For instance, `Option`'s effect +abstracts away potentially missing values, where `map` applies the function only in the `Some` case but +otherwise threads the `None` through. -This example demonstrates the use of the -[kind-projector compiler plugin](https://github.com/non/kind-projector). -This compiler plugin can help us when we need to change the number of type -holes. In the example above, we took a type which normally has two type holes, -`Function1[?,?]` and constrained one of the holes to be the `In` type, -leaving just one hole for the return type, resulting in `Function1[In,?]`. -Without kind-projector, we'd have to write this as something like -`({type F[A] = Function1[In,A]})#F`, which is much harder to read and understand. +Taking this view, we can view `Functor` as the ability to work with a **single** effect - we can apply a pure +function to a single effectful value without needing to "leave" the effect. -## Using Functor +## Functors compose -### map +If you're ever found yourself working with nested data types such as `Option[List[A]]` or a +`List[Either[String, Future[A]]]` and tried to `map` over it, you've most likely found yourself doing something +like `_.map(_.map(_.map(f)))`. As it turns out, `Functor`s compose, which means if `F` and `G` have +`Functor` instances, then so does `F[G[_]]`. -`List` is a functor which applies the function to each element of the list: +Such composition can be achieved via the `Functor#compose` method. -```tut:book -val len: String => Int = _.length -Functor[List].map(List("qwer", "adsfg"))(len) +```tut:reset:book:silent +import cats.Functor +import cats.instances.list._ +import cats.instances.option._ ``` -`Option` is a functor which only applies the function when the `Option` value -is a `Some`: - ```tut:book -Functor[Option].map(Some("adsf"))(len) // Some(x) case: function is applied to x; result is wrapped in Some -Functor[Option].map(None)(len) // None case: simply returns None (function is not applied) -``` - -## Derived methods +val listOption = List(Some(1), None, Some(2)) -### lift - -We can use `Functor` to "lift" a function from `A => B` to `F[A] => F[B]`: - -```tut:book -val lenOption: Option[String] => Option[Int] = Functor[Option].lift(len) -lenOption(Some("abcd")) +// Through Functor#compose +Functor[List].compose[Option].map(listOption)(_ + 1) ``` -### fproduct +This approach will allow us to use composition without wrapping the value in question, but can +introduce complications in more complex use cases. For example, if we need to call another function which +requires a `Functor` and we want to use the composed `Functor`, we would have to explicitly pass in the +composed instance during the function call or create a local implicit. -`Functor` provides an `fproduct` function which pairs a value with the -result of applying a function to that value. +```tut:book:silent +def needsFunctor[F[_]: Functor, A](fa: F[A]): F[Unit] = Functor[F].map(fa)(_ => ()) -```tut:book -val source = List("a", "aa", "b", "ccccc") -Functor[List].fproduct(source)(len).toMap +def foo: List[Option[Unit]] = { + val listOptionFunctor = Functor[List].compose[Option] + type ListOption[A] = List[Option[A]] + needsFunctor[ListOption, Int](listOption)(listOptionFunctor) +} ``` -### compose - -Functors compose! Given any functor `F[_]` and any functor `G[_]` we can -create a new functor `F[G[_]]` by composing them via the `Nested` data type: +We can make this nicer at the cost of boxing with the `Nested` data type. -```tut:book +```tut:book:silent import cats.data.Nested -val listOpt = Nested[List, Option, Int](List(Some(1), None, Some(3))) -Functor[Nested[List, Option, ?]].map(listOpt)(_ + 1) - -val optList = Nested[Option, List, Int](Some(List(1, 2, 3))) -Functor[Nested[Option, List, ?]].map(optList)(_ + 1) +import cats.syntax.functor._ ``` -## Subtyping - -Functors have a natural relationship with subtyping: - ```tut:book -class A -class B extends A -val b: B = new B -val a: A = b -val listB: List[B] = List(new B) -val listA1: List[A] = listB.map(b => b: A) -val listA2: List[A] = listB.map(identity[A]) -val listA3: List[A] = Functor[List].widen[B, A](listB) +val nested: Nested[List, Option, Int] = Nested(listOption) + +nested.map(_ + 1) ``` -Subtyping relationships are "lifted" by functors, such that if `F` is a -lawful functor and `A <: B` then `F[A] <: F[B]` - almost. Almost, because to -convert an `F[B]` to an `F[A]` a call to `map(identity[A])` is needed -(provided as `widen` for convenience). The functor laws guarantee that -`fa map identity == fa`, however. +The `Nested` approach, being a distinct type from its constituents, will resolve the usual way modulo +possible [SI-2712][si2712] issues, but requires syntactic and runtime overhead from wrapping and +unwrapping. + +[si2712]: https://issues.scala-lang.org/browse/SI-2712 "SI-2712: implement higher-order unification for type constructor inference" diff --git a/docs/src/main/tut/typeclasses/traverse.md b/docs/src/main/tut/typeclasses/traverse.md index ea892f8ea4..03d0910a05 100644 --- a/docs/src/main/tut/typeclasses/traverse.md +++ b/docs/src/main/tut/typeclasses/traverse.md @@ -5,235 +5,165 @@ section: "typeclasses" source: "core/src/main/scala/cats/Traverse.scala" scaladoc: "#cats.Traverse" --- -# Traverse -In functional programming it is very common to encode "effects" as data types - common effects -include `Option` for possibly missing values, `Either` and `Validated` for possible errors, and -`Future` for asynchronous computations. - -These effects tend to show up in functions working on a single piece of data - for instance -parsing a single `String` into an `Int`, validating a login, or asynchronously fetching website -information for a user. - -```tut:silent -import scala.concurrent.Future - -def parseInt(s: String): Option[Int] = ??? -trait SecurityError -trait Credentials +# Traverse -def validateLogin(cred: Credentials): Either[SecurityError, Unit] = ??? +In the `Applicative` tutorial we saw a more polymorphic version of the standard library +`Future.traverse` and `Future.sequence` functions, generalizing `Future` to be any +`F[_]` that's `Applicative`. -trait Profile -trait User +```tut:book:silent +import cats.Applicative -def userInfo(user: User): Future[Profile] = ??? +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)(_ :: _) + } ``` -Each function asks only for the data it actually needs; in the case of `userInfo`, a single `User`. We -certainly could write one that takes a `List[User]` and fetch profile for all of them, would be a bit strange. -If we just wanted to fetch the profile of just one user, we would either have to wrap it in a `List` or write -a separate function that takes in a single user anyways. More fundamentally, functional programming is about -building lots of small, independent pieces and composing them to make larger and larger pieces - does this -hold true in this case? - -Given just `User => Future[Profile]`, what should we do if we want to fetch profiles for a `List[User]`? -We could try familiar combinators like `map`. +Here `traverse` still has knowledge of `List`, but we could just as easily use +`Vector` or similar data type. Another example is a binary tree: + +```tut:book:silent +object tree { + sealed abstract class Tree[A] extends Product with Serializable { + def traverse[F[_]: Applicative, B](f: A => F[B]): F[Tree[B]] = this match { + case Tree.Empty() => Applicative[F].pure(Tree.Empty()) + case Tree.Branch(v, l, r) => Applicative[F].map3(f(v), l.traverse(f), r.traverse(f))(Tree.Branch(_, _, _)) + } + } + + object Tree { + final case class Empty[A]() extends Tree[A] + final case class Branch[A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] + } +} -```tut:silent -def profilesFor(users: List[User]): List[Future[Profile]] = users.map(userInfo) +import tree._ ``` -Note the return type `List[Future[Profile]]`. This makes sense given the type signatures, but seems unwieldy. -We now have a list of asynchronous values, and to work with those values we must then use the combinators on -`Future` for every single one. It would be nicer instead if we could get the aggregate result in a single -`Future`, say a `Future[List[Profile]]`. - -As it turns out, the `Future` companion object has a `traverse` method on it. However, that method is -specialized to standard library collections and `Future`s - there exists a much more generalized form -that would allow us to parse a `List[String]` or validate credentials for a `List[User]`. +This suggests an abstraction over "things that can be traversed over," hence `Traverse`. -Enter `Traverse`. - -## The type class -At center stage of `Traverse` is the `traverse` method. - -```scala +```tut:book:silent trait Traverse[F[_]] { - def traverse[G[_] : Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] + def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] } -``` - -In our above example, `F` is `List`, and `G` is `Option`, `Either`, or `Future`. For the profile example, -`traverse` says given a `List[User]` and a function `User => Future[Profile]`, it can give you a -`Future[List[Profile]]`. -Abstracting away the `G` (still imagining `F` to be `List`), `traverse` says given a collection of data, -and a function that takes a piece of data and returns an effectful value, it will traverse the collection, -applying the function and aggregating the effectful values (in a `List`) as it goes. - -In the most general form, `F[_]` is some sort of context which may contain a value (or several). While -`List` tends to be among the most general cases, there also exist `Traverse` instances for `Option`, -`Either`, and `Validated` (among others). +// Example implementation for List +implicit val traverseForList: Traverse[List] = new Traverse[List] { + def traverse[G[_]: Applicative, A, B](fa: List[A])(f: A => G[B]): G[List[B]] = + fa.foldRight(Applicative[G].pure(List.empty[B])) { (a, acc) => + Applicative[G].map2(f(a), acc)(_ :: _) + } +} -## Choose your effect -The type signature of `Traverse` appears highly abstract, and indeed it is - what `traverse` does as it -walks the `F[A]` depends on the effect of the function. Let's see some examples where `F` is taken to be -`List`. +// Example implementation for Tree +implicit val traverseForTree: Traverse[Tree] = new Traverse[Tree] { + def traverse[G[_]: Applicative, A, B](fa: Tree[A])(f: A => G[B]): G[Tree[B]] = + fa.traverse(f) +} +``` -Note in the following code snippet we are using `traverseU` instead of `traverse`. -`traverseU` is for all intents and purposes the same as `traverse`, but with some -[type-level trickery](http://typelevel.org/blog/2013/09/11/using-scalaz-Unapply.html) -to allow it to infer the `Applicative[Either[A, ?]]` and `Applicative[Validated[A, ?]]` -instances - `scalac` has issues inferring the instances for data types that do not -trivially satisfy the `F[_]` shape required by `Applicative`. +## A note on sequencing -```tut:silent -import cats.Semigroup -import cats.data.{NonEmptyList, OneAnd, Validated, ValidatedNel} -import cats.implicits._ +Sometimes you will be given a traversable that has effectful values already, such as +a `List[Option[A]]`. Since the values themselves are effects, traversing with `identity` +will turn the traversable "inside out." -def parseIntEither(s: String): Either[NumberFormatException, Int] = - Either.catchOnly[NumberFormatException](s.toInt) +```tut:reset:book:silent +import cats.instances.list._ +import cats.instances.option._ +import cats.syntax.traverse._ +``` -def parseIntValidated(s: String): ValidatedNel[NumberFormatException, Int] = - Validated.catchOnly[NumberFormatException](s.toInt).toValidatedNel +```tut:book +val list = List(Some(1), Some(2), None) +val traversed = list.traverse(identity) ``` -Examples. +Cats provides a convenience method for this called `sequence`. ```tut:book -val x1 = List("1", "2", "3").traverseU(parseIntEither) -val x2 = List("1", "abc", "3").traverseU(parseIntEither) -val x3 = List("1", "abc", "def").traverseU(parseIntEither) - -val v1 = List("1", "2", "3").traverseU(parseIntValidated) -val v2 = List("1", "abc", "3").traverseU(parseIntValidated) -val v3 = List("1", "abc", "def").traverseU(parseIntValidated) +val sequenced = list.sequence ``` -Notice that in the `Either` case, should any string fail to parse the entire traversal -is considered a failure. Moreover, once it hits its first bad parse, it will not -attempt to parse any others down the line (similar behavior would be found with -using `Option` as the effect). Contrast this with `Validated` where even -if one bad parse is hit, it will continue trying to parse the others, accumulating -any and all errors as it goes. The behavior of traversal is closely tied with the -`Applicative` behavior of the data type. +In general `t.map(f).sequence` can be replaced with `t.traverse(f)`. -Going back to our `Future` example, we can write an `Applicative` instance for -`Future` that runs each `Future` concurrently. Then when we traverse a `List[A]` -with an `A => Future[B]`, we can imagine the traversal as a scatter-gather. -Each `A` creates a concurrent computation that will produce a `B` (the scatter), -and as the `Future`s complete they will be gathered back into a `List`. +## Traversables are Functors -### Playing with `Reader` -Another interesting effect we can use is `Reader`. Recall that a `Reader[E, A]` is -a type alias for `Kleisli[Id, E, A]` which is a wrapper around `E => A`. +As it turns out every `Traverse` is a lawful `Functor`. By carefully picking the `G` to +use in `traverse` we can implement `map`. -If we fix `E` to be some sort of environment or configuration, we can use the -`Reader` applicative in our traverse. +First let's look at the two signatures. -```tut:silent -import cats.data.Reader +```tut:book:silent +import cats.{Applicative, Traverse} -trait Context -trait Topic -trait Result +def traverse[F[_]: Traverse, G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] = ??? -type Job[A] = Reader[Context, A] - -def processTopic(topic: Topic): Job[Result] = ??? +def map[F[_]: Traverse, A, B](fa: F[A])(f: A => B): F[B] = ??? ``` -We can imagine we have a data pipeline that processes a bunch of data, each piece of data -being categorized by a topic. Given a specific topic, we produce a `Job` that processes -that topic. (Note that since a `Job` is just a `Reader`/`Kleisli`, one could write many small -`Job`s and compose them together into one `Job` that is used/returned by `processTopic`.) - -Corresponding to our bunches of data are bunches of topics, a `List[Topic]` if you will. -Since `Reader` has an `Applicative` instance, we can `traverse` over this list with `processTopic`. +Both have an `F[A]` parameter and a similar `f` parameter. `traverse` expects the return type +of `f` to be `G[B]` whereas `map` just wants `B`. Similarly the return type of `traverse` is +`G[F[B]]` whereas for `map` it's just `F[B]`. This suggests we need to pick a `G` such that +`G[A]` communicates exactly as much information as `A`. We can conjure one up by simply wrapping +an `A`. -```tut:silent -def processTopics(topics: List[Topic]): Job[List[Result]] = - topics.traverse(processTopic) +```tut:book:silent +final case class Id[A](value: A) ``` -Note the nice return type - `Job[List[Result]]`. We now have one aggregate `Job` that when run, -will go through each topic and run the topic-specific job, collecting results as it goes. -We say "when run" because a `Job` is some function that requires a `Context` before producing -the value we want. - -One example of a "context" can be found in the [Spark](http://spark.apache.org/) project. In -Spark, information needed to run a Spark job (where the master node is, memory allocated, etc.) -resides in a `SparkContext`. Going back to the above example, we can see how one may define -topic-specific Spark jobs (`type Job[A] = Reader[SparkContext, A]`) and then run several -Spark jobs on a collection of topics via `traverse`. We then get back a `Job[List[Result]]`, -which is equivalent to `SparkContext => List[Result]`. When finally passed a `SparkContext`, -we can run the job and get our results back. - -Moreover, the fact that our aggregate job is not tied to any specific `SparkContext` allows us -to pass in a `SparkContext` pointing to a production cluster, or (using the exact same job) pass -in a test `SparkContext` that just runs locally across threads. This makes testing our large -job nice and easy. - -Finally, this encoding ensures that all the jobs for each topic run on the exact same cluster. -At no point do we manually pass in or thread a `SparkContext` through - that is taken care for us -by the (applicative) effect of `Reader` and therefore by `traverse`. - -## Sequencing -Sometimes you may find yourself with a collection of data, each of which is already in an effect, -for instance a `List[Option[A]]`. To make this easier to work with, you want a `Option[List[A]]`. -Given `Option` has an `Applicative` instance, we can traverse over the list with the identity function. +In order to call `traverse` `Id` needs to be `Applicative` which is straightforward - note that while +`Id` just wraps an `A`, it is still a type constructor which matches the shape required by `Applicative`. -```tut:book -import cats.implicits._ -val l1 = List(Option(1), Option(2), Option(3)).traverse(identity) -val l2 = List(Option(1), None, Option(3)).traverse(identity) +```tut:book:silent +implicit val applicativeForId: Applicative[Id] = new Applicative[Id] { + def ap[A, B](ff: Id[A => B])(fa: Id[A]): Id[B] = Id(ff.value(fa.value)) + + def pure[A](a: A): Id[A] = Id(a) +} ``` -`Traverse` provides a convenience method `sequence` that does exactly this. +Now we can implement `map` by wrapping and unwrapping `Id` as necessary. -```tut:book -val l1 = List(Option(1), Option(2), Option(3)).sequence -val l2 = List(Option(1), None, Option(3)).sequence -``` +```tut:book:silent +import cats.Functor -## Traversing for effect -Sometimes our effectful functions return a `Unit` value in cases where there is no interesting value -to return (e.g. writing to some sort of store). +trait Traverse[F[_]] extends Functor[F] { + def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] -```tut:silent -trait Data -def writeToStore(data: Data): Future[Unit] = ??? + def map[A, B](fa: F[A])(f: A => B): F[B] = + traverse(fa)(a => Id(f(a))).value +} ``` -If we traverse using this, we end up with a funny type. +`Id` is provided in Cats as a type alias `type Id[A] = A`. -```tut:silent -import cats.implicits._ -import scala.concurrent.ExecutionContext.Implicits.global +## Traversables are Foldable -def writeManyToStore(data: List[Data]) = - data.traverse(writeToStore) -``` +The `Foldable` type class abstracts over "things that can be folded over" similar to how +`Traverse` abstracts over "things that can be traversed." It turns out `Traverse` is strictly +more powerful than `Foldable` - that is, `foldLeft` and `foldRight` can be implemented +in terms of `traverse` by picking the right `Applicative`. However, `cats.Traverse` does not +implement `foldLeft` and `foldRight` as the actual implementation tends to be ineffecient. -We end up with a `Future[List[Unit]]`! A `List[Unit]` is not of any use to us, and communicates the -same amount of information as a single `Unit` does. +For brevity and demonstration purposes we'll implement the equivalent `foldMap` method in terms +of `traverse` by using `cats.data.Const`. You can then implement `foldRight` in terms of `foldMap`, +and `foldLeft` can then be implemented in terms of `foldRight`, though the resulting implementations +may be slow. -Traversing solely for the sake of the effect (ignoring any values that may be produced, `Unit` or otherwise) -is common, so `Foldable` (superclass of `Traverse`) provides `traverse_` and `sequence_` methods that do the -same thing as `traverse` and `sequence` but ignores any value produced along the way, returning `Unit` at the end. +```tut:reset:book:silent +import cats.{Applicative, Monoid, Traverse} +import cats.data.Const -```tut:silent -import cats.implicits._ +def foldMap[F[_]: Traverse, A, B: Monoid](fa: F[A])(f: A => B): B = + Traverse[F].traverse[Const[B, ?], A, B](fa)(a => Const(f(a))).getConst +``` -def writeManyToStore(data: List[Data]) = - data.traverse_(writeToStore) +## Further Reading -// Int values are ignored with traverse_ -def writeToStoreV2(data: Data): Future[Int] = - ??? +* [The Essence of the Iterator Pattern][iterator] - Gibbons, Oliveira. JFP 2009. -def writeManyToStoreV2(data: List[Data]) = - data.traverse_(writeToStoreV2) -``` +[iterator]: https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf "The Essence of the Iterator Pattern"