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

MonadError instance for Kleisli #1543

Merged
merged 1 commit into from
Mar 6, 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
91 changes: 58 additions & 33 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,29 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 {
}

private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 {

implicit def catsDataMonadErrorForKleisli[F[_], A, E](
implicit me: MonadError[F, E]
): MonadError[Kleisli[F, A, ?], E] with MonadReader[Kleisli[F, A, ?], A] = {
new KleisliApplicativeError[F, A, E]
with KleisliMonadReader[F, A]
with MonadError[Kleisli[F, A, ?], E] {
override def M: Monad[F] = me
override def AF: ApplicativeError[F, E] = me
}
}
}

private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 {

implicit def catsDataMonadReaderForKleisli[F[_]: Monad, A](implicit m: Monad[F]): MonadReader[Kleisli[F, A, ?], A] = {
new KleisliMonadReader[F, A] {
override def M: Monad[F] = m
}
}
}

private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 {
implicit def catsDataChoiceForKleisli[F[_]](implicit ev: Monad[F]): Choice[Kleisli[F, ?, ?]] =
new Choice[Kleisli[F, ?, ?]] {
def id[A]: Kleisli[F, A, A] = Kleisli(ev.pure)
Expand All @@ -138,16 +161,8 @@ private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1
implicit def catsDataStrongForKleisli[F[_]](implicit ev: Functor[F]): Strong[Kleisli[F, ?, ?]] =
new KleisliStrong[F] { def F: Functor[F] = ev }

implicit def catsDataFlatMapForKleisli[F[_]: FlatMap, A]: FlatMap[Kleisli[F, A, ?]] = new FlatMap[Kleisli[F, A, ?]] {
def flatMap[B, C](fa: Kleisli[F, A, B])(f: B => Kleisli[F, A, C]): Kleisli[F, A, C] =
fa.flatMap(f)

def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] =
fa.map(f)

def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Either[B, C]]): Kleisli[F, A, C] =
Kleisli[F, A, C]({ a => FlatMap[F].tailRecM(b) { f(_).run(a) } })
}
implicit def catsDataFlatMapForKleisli[F[_], A](implicit ev: FlatMap[F]): FlatMap[Kleisli[F, A, ?]] =
new KleisliFlatMap[F, A] { def FM: FlatMap[F] = ev }

implicit def catsDataSemigroupForKleisli[F[_], A, B](implicit M: Semigroup[F[B]]): Semigroup[Kleisli[F, A, B]] =
new KleisliSemigroup[F, A, B] { def FB: Semigroup[F[B]] = M }
Expand All @@ -156,13 +171,13 @@ private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1
Compose[Kleisli[F, ?, ?]].algebraK
}

private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 {
private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 {
implicit def catsDataApplicativeForKleisli[F[_], A](implicit A : Applicative[F]): Applicative[Kleisli[F, A, ?]] = new KleisliApplicative[F, A] {
implicit def F: Applicative[F] = A
}
}

private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 {
private[data] sealed abstract class KleisliInstances4 extends KleisliInstances5 {
implicit def catsDataApplyForKleisli[F[_]: Apply, A]: Apply[Kleisli[F, A, ?]] = new Apply[Kleisli[F, A, ?]] {
def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] =
fa.ap(f)
Expand All @@ -175,33 +190,13 @@ private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3
}
}

private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 {
private[data] sealed abstract class KleisliInstances5 {
implicit def catsDataFunctorForKleisli[F[_]: Functor, A]: Functor[Kleisli[F, A, ?]] = new Functor[Kleisli[F, A, ?]] {
def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] =
fa.map(f)
}
}

private[data] sealed abstract class KleisliInstances4 {

implicit def catsDataMonadReaderForKleisli[F[_]: Monad, A]: MonadReader[Kleisli[F, A, ?], A] =
new MonadReader[Kleisli[F, A, ?], A] {
def pure[B](x: B): Kleisli[F, A, B] =
Kleisli.pure[F, A, B](x)

def flatMap[B, C](fa: Kleisli[F, A, B])(f: B => Kleisli[F, A, C]): Kleisli[F, A, C] =
fa.flatMap(f)

val ask: Kleisli[F, A, A] = Kleisli(Monad[F].pure)

def local[B](f: A => A)(fa: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli(f.andThen(fa.run))

def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Either[B, C]]): Kleisli[F, A, C] =
Kleisli[F, A, C]({ a => FlatMap[F].tailRecM(b) { f(_).run(a) } })
}
}

private trait KleisliArrow[F[_]] extends Arrow[Kleisli[F, ?, ?]] with KleisliSplit[F] with KleisliStrong[F] {
implicit def F: Monad[F]

Expand Down Expand Up @@ -275,6 +270,36 @@ private trait KleisliApplicativeError[F[_], A, E] extends KleisliApplicative[F,

}

private trait KleisliMonadReader[F[_], A] extends KleisliFlatMap[F, A] with KleisliApplicative[F, A] with MonadReader[Kleisli[F, A, ?], A] {

def M: Monad[F]

override def F: Applicative[F] = M
override def FM: FlatMap[F] = M

val ask: Kleisli[F, A, A] = Kleisli(M.pure)

def local[B](f: A => A)(fa: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli(f.andThen(fa.run))

override def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] =
super[KleisliApplicative].ap(f)(fa)
}

private trait KleisliFlatMap[F[_], A] extends FlatMap[Kleisli[F, A, ?]] {

implicit def FM: FlatMap[F]

def flatMap[B, C](fa: Kleisli[F, A, B])(f: B => Kleisli[F, A, C]): Kleisli[F, A, C] =
fa.flatMap(f)

def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] =
fa.map(f)

def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Either[B, C]]): Kleisli[F, A, C] =
Kleisli[F, A, C]({ a => FlatMap[F].tailRecM(b) { f(_).run(a) } })
}
Copy link
Collaborator

@peterneyens peterneyens Mar 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this we have the same code twice:

  • here in KleisliFlatMap
  • and in catsDataMonadReaderForKleisli

Maybe we could create a KleisliMonad which can be extended by a KleisliMonadReader and a KleisliMonadError (similar as how it is done in StateT) ?


private trait KleisliApplicative[F[_], A] extends Applicative[Kleisli[F, A, ?]] {
implicit def F: Applicative[F]

Expand Down
10 changes: 10 additions & 0 deletions tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class KleisliTests extends CatsSuite {
checkAll("Kleisli[Option, Int, Int] with Unit", ApplicativeErrorTests[Kleisli[Option, Int, ?], Unit].applicativeError[Int, Int, Int])
checkAll("ApplicativeError[Kleisli[Option, Int, Int], Unit]", SerializableTests.serializable(ApplicativeError[Kleisli[Option, Int, ?], Unit]))

checkAll("Kleisli[Option, Int, Int] with Unit", MonadErrorTests[Kleisli[Option, Int, ?], Unit].monadError[Int, Int, Int])
checkAll("MonadError[Kleisli[Option, Int, Int], Unit]", SerializableTests.serializable(MonadError[Kleisli[Option, Int, ?], Unit]))

checkAll("Kleisli[Option, Int, Int]", CartesianTests[Kleisli[Option, Int, ?]].cartesian[Int, Int, Int])
checkAll("Cartesian[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Cartesian[Kleisli[Option, Int, ?]]))

Expand Down Expand Up @@ -206,5 +209,12 @@ class KleisliTests extends CatsSuite {
Monoid[IntReader[String]]
FlatMap[IntReader]
Semigroup[IntReader[String]]

// ApplicativeError and MonadError (some SI-2712 workarounds are needed)
type UnitValidated[A] = cats.data.Validated[Unit, A]
type KleisliUV[A] = Kleisli[UnitValidated, Int, A]
ApplicativeError[KleisliUV, Unit]
ApplicativeError[Kleisli[Option, Int, ?], Unit]
MonadError[Kleisli[Option, Int, ?], Unit]
}
}