From 67ca7b93a69f89c5f1f3d93546522cb6db4a68d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Koz=C5=82owski?= Date: Tue, 18 Dec 2018 16:08:58 +0100 Subject: [PATCH] Add example of unlawful Applicative for Nested/EitherT --- docs/src/main/tut/guidelines.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/src/main/tut/guidelines.md b/docs/src/main/tut/guidelines.md index 3682be22da..58a1ee88c5 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,14 +113,28 @@ 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. `OptionT[F, ?]` given an `Applicative[F]`. +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 `OptionT[F, ?]`. See [#1467](https://github.com/typelevel/cats/issues/1467) for the discussion. +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: