diff --git a/docs/src/main/tut/typeclasses/bifunctor.md b/docs/src/main/tut/typeclasses/bifunctor.md new file mode 100644 index 0000000000..4f3bc24083 --- /dev/null +++ b/docs/src/main/tut/typeclasses/bifunctor.md @@ -0,0 +1,52 @@ +--- +layout: docs +title: "Bifunctor" +section: "typeclasses" +source: "core/src/main/scala/cats/Bifunctor.scala" +scaladoc: "#cats.Bifunctor" +--- +# Bifunctor + +`Bifunctor` takes two type parameters instead of one, and is a functor in both +of these parameters. It defines a function `bimap`, which allows for mapping over both +arguments at the same time. Its signature is as follows: + +```scala +def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D] +``` + +## Either as a Bifunctor + +Probably the most widely used Bifunctor instance is the Either data type. + +Say you have a value that is either an error or a `ZonedDateTime` instance. +You also want to react to both possibilities - if there was a failure, you want to +convert it to your own `DomainError`, and if the result was a success, you want to +convert it to an UNIX timestamp. + +```tut:silent +import cats._ +import cats.implicits._ +import java.time._ + +case class DomainError(message: String) + +def dateTimeFromUser: Either[Throwable, ZonedDateTime] = + Right(ZonedDateTime.now()) // Example definition +``` + +```tut:book +dateTimeFromUser.bimap( + error => DomainError(error.getMessage), + dateTime => dateTime.toEpochSecond +) +``` + +`Bifunctor` also defines a convenience function called `leftMap`, which is defined as follows: + +```scala +def leftMap[A, B, C](fab: F[A, B])(f: A => C): F[C, B] = bimap(fab)(f, identity) +``` + +There is no `rightMap` however - use `map` instead. The reasoning behind this is that in Cats, the instances of +`Bifunctor` are also mostly instances of `Functor`, as it is the case with `Either`. diff --git a/docs/src/main/tut/typeclasses/monad.md b/docs/src/main/tut/typeclasses/monad.md index 57b41343f9..c69ee805fe 100644 --- a/docs/src/main/tut/typeclasses/monad.md +++ b/docs/src/main/tut/typeclasses/monad.md @@ -159,3 +159,26 @@ implicit def optionTMonad[F[_]](implicit F : Monad[F]) = { This sort of construction is called a monad transformer. Cats has an [`OptionT`](optiont.html) monad transformer, which adds a lot of useful functions to the simple implementation above. + +## FlatMap - a weakened Monad +A closely related type class is `FlatMap` which is identical to `Monad`, minus the `pure` +method. Indeed in Cats `Monad` is a subclass of `FlatMap` (from which it gets `flatMap`) +and `Applicative` (from which it gets `pure`). + +```scala +trait FlatMap[F[_]] extends Apply[F] { + def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] +} + +trait Monad[F[_]] extends FlatMap[F] with Applicative[F] +``` + +The laws for `FlatMap` are just the laws of `Monad` that don't mention `pure`. + +One of the motivations for `FlatMap`'s existence is that some types have `FlatMap` instances but not +`Monad` - 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 `FlatMap` instance. +