diff --git a/docs/src/main/tut/faq.md b/docs/src/main/tut/faq.md index a886aecde1..408c9d9a68 100644 --- a/docs/src/main/tut/faq.md +++ b/docs/src/main/tut/faq.md @@ -17,6 +17,7 @@ position: 40 * [Why is some example code not compiling for me?](#example-compile) * [How can I turn my List of `` into a `` of a list?](#traverse) * [Where is `ListT`?](#listt) + * [Where are `Applicative`s for monad transformers?(#applicative-monad-transformers) * [Where is `IO`/`Task`?](#task) * [What does `@typeclass` mean?](#simulacrum) * [What do types like `?` and `λ` mean?](#kind-projector) @@ -124,6 +125,12 @@ def even(i: Int): ErrorsOr[Int] = if (i % 2 == 0) i.validNel else s"$i is odd".i nl.traverse(even) ``` +## Where are `Applicative`s for monad transformers? + +An `Applicative` instance for `OptionT[F, ?]`/`EitherT[F, E, ?]`, built without a corresponding `Monad` instance for `F`, would be unlawful, so it's not included. See [https://typelevel.org/cats/guidelines.html#applicative-monad-transformers](the guidelines) for a more detailed explanation. + +As an alternative, using `.toNested` on the monad transformer is recommended, although its `ap` will still be inconsistent with the Monad instance's.`. + ## Where is IO/Task? In purely functional programming, a monadic `IO` or `Task` type is often used to handle side effects such as file/network IO. In some languages and frameworks, such a type also serves as the primary abstraction through which parallelism is achieved. Nearly every real-world purely functional application or service is going to require such a data type, and this gives rise to an obvious question: why doesn't cats include such a type? diff --git a/docs/src/main/tut/guidelines.md b/docs/src/main/tut/guidelines.md index 2ebad93c81..f6589d45f5 100644 --- a/docs/src/main/tut/guidelines.md +++ b/docs/src/main/tut/guidelines.md @@ -88,19 +88,19 @@ This rule is relatively flexible. Use what you see appropriate. The goal is to m ### Implicit instance priority -When there are multiple instances provided implicitly, if the type class of them are in the same inheritance hierarchy, +When there are multiple instances provided implicitly, if the type class of them are in the same inheritance hierarchy, the instances need to be separated out into different abstract class/traits so that they don't conflict with each other. The names of these abstract classes/traits should be numbered with a priority with 0 being the highest priority. The abstract classes/trait -with higher priority inherits from the ones with lower priority. The most specific (whose type class is the lowest in the hierarchy) instance should be placed in the abstract class/ trait with the highest priority. Here is an example. +with higher priority inherits from the ones with lower priority. The most specific (whose type class is the lowest in the hierarchy) instance should be placed in the abstract class/ trait with the highest priority. Here is an example. ```scala -@typeclass +@typeclass trait Functor[F[_]] @typeclass trait Monad[F[_]] extends Functor ... -object Kleisli extends KleisliInstance0 +object Kleisli extends KleisliInstance0 abstract class KleisliInstance0 extends KleisliInstance1 { implicit def catsDataMonadForKleisli[F[_], A]: Monad[Kleisli[F, A, ?]] = ... @@ -113,9 +113,29 @@ abstract class KleisliInstance1 { ### Type classes that ONLY define laws. -We can introduce new type classes for the sake of adding laws that don't apply to the parent type class, e.g. `CommutativeSemigroup` and +We can introduce new type classes for the sake of adding laws that don't apply to the parent type class, e.g. `CommutativeSemigroup` and `CommutativeArrow`. +### Applicative instances for monad transformers + +We explicitly don't provide an instance of `Applicative` for e.g. `EitherT[F, String, ?]` given an `Applicative[F]`. +An attempt to construct one without a proper `Monad[F]` instance would be inconsistent in `ap` with the provided `Monad` instance +for `EitherT[F, String, ?]`. Such an instance will be derived if you use `Nested` instead: + +```scala +import cats._, cats.data._, cats.implicits._ + +val a = EitherT(List(Left("err"), Right(1))) +val x = (a *> a).value +> x: List[Either[String, Int]] = List(Left("err"), Left("err"), Right(1)) + +val y = (a.toNested *> a.toNested).value +> y: List[Either[String, Int]] = List(Left("err"), Left("err"), Left("err"), Right(1)) + +x === y +> false +``` + #### TODO: Once we drop 2.10 support, AnyVal-extending class constructor parameters can be marked as private.