diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 1fbdf1e5f7..069b92fe82 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -123,6 +123,9 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { def liftT[M[_], B](ma: M[B])(implicit ev: Trivial): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) } + + implicit def kleisliApplicativeError[F[_], A, E](implicit AE: ApplicativeError[F, E]): ApplicativeError[Kleisli[F, A, ?], E] + = new KleisliApplicativeError[F, A, E] { implicit def AF: ApplicativeError[F, E] = AE } } private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 { @@ -145,21 +148,12 @@ private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 implicit def kleisliSemigroupK[F[_]](implicit ev: FlatMap[F]): SemigroupK[Lambda[A => Kleisli[F, A, A]]] = new KleisliSemigroupK[F] { def F: FlatMap[F] = ev } + } private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 { - implicit def kleisliApplicative[F[_]: Applicative, A]: Applicative[Kleisli[F, A, ?]] = new Applicative[Kleisli[F, A, ?]] { - def pure[B](x: B): Kleisli[F, A, B] = - Kleisli.pure[F, A, B](x) - - def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = - fa.ap(f) - - override def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = - fb.map(f) - - override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = - Kleisli(a => Applicative[F].product(fb.run(a), fc.run(a))) + implicit def kleisliApplicative[F[_], A](implicit A : Applicative[F]): Applicative[Kleisli[F, A, ?]] = new KleisliApplicative[F, A] { + implicit def F: Applicative[F] = A } } @@ -269,3 +263,36 @@ private trait KleisliMonoidK[F[_]] extends MonoidK[Lambda[A => Kleisli[F, A, A]] override def empty[A]: Kleisli[F, A, A] = Kleisli(F.pure[A]) } + + +private trait KleisliApplicativeError[F[_], A, E] extends KleisliApplicative[F, A] with ApplicativeError[Kleisli[F, A, ?], E] { + type K[T] = Kleisli[F, A, T] + + implicit def AF: ApplicativeError[F, E] + + implicit def F: Applicative[F] = AF + + def raiseError[B](e: E): K[B] = Kleisli(_ => AF.raiseError(e)) + + def handleErrorWith[B](kb: K[B])(f: E => K[B]): K[B] = Kleisli { a: A => + AF.handleErrorWith(kb.run(a))((e: E) => f(e).run(a)) + } + +} + + +private trait KleisliApplicative[F[_], A] extends Applicative[Kleisli[F, A, ?]] { + implicit def F: Applicative[F] + + def pure[B](x: B): Kleisli[F, A, B] = + Kleisli.pure[F, A, B](x) + + def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = + fa.ap(f) + + override def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = + fb.map(f) + + override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = + Kleisli(a => Applicative[F].product(fb.run(a), fc.run(a))) +} diff --git a/core/src/main/scala/cats/std/option.scala b/core/src/main/scala/cats/std/option.scala index 96c6d76658..7ffef7c4bb 100644 --- a/core/src/main/scala/cats/std/option.scala +++ b/core/src/main/scala/cats/std/option.scala @@ -3,8 +3,8 @@ package std trait OptionInstances extends cats.kernel.std.OptionInstances { - implicit val optionInstance: Traverse[Option] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = - new Traverse[Option] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { + implicit val optionInstance: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = + new Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { def empty[A]: Option[A] = None @@ -48,6 +48,10 @@ trait OptionInstances extends cats.kernel.std.OptionInstances { case Some(a) => Applicative[G].map(f(a))(Some(_)) } + def raiseError[A](e: Unit): Option[A] = None + + def handleErrorWith[A](fa: Option[A])(f: (Unit) => Option[A]): Option[A] = fa orElse f(()) + override def exists[A](fa: Option[A])(p: A => Boolean): Boolean = fa.exists(p) diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index 27350621db..c8f83db8fc 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.arrow.{Arrow, Choice, Split} -import cats.data.{Kleisli, Reader} +import cats.data.{XorT, Kleisli, Reader} import cats.functor.{Contravariant, Strong} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ @@ -15,8 +15,13 @@ class KleisliTests extends CatsSuite { implicit def kleisliEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[Kleisli[F, A, B]] = Eq.by[Kleisli[F, A, B], A => F[B]](_.run) + implicit val xorTEq = XorT.xorTEq[Kleisli[Option, Int, ?], Unit, Int] + implicit val iso = CartesianTests.Isomorphisms.invariant[Kleisli[Option, Int, ?]] + checkAll("Klesili[Option, Int, Int] with Unit", ApplicativeErrorTests[Kleisli[Option, Int, ?], Unit].applicativeError[Int, Int, Int]) + checkAll("ApplicativeError[Klesili[Option, Int, Int], Unit]", SerializableTests.serializable(ApplicativeError[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, ?]])) diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index 01ab65b3c7..8c6ef464b9 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.laws.{ApplicativeLaws, CoflatMapLaws, FlatMapLaws, MonadLaws} -import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, CartesianTests} +import cats.laws.discipline._ class OptionTests extends CatsSuite { checkAll("Option[Int]", CartesianTests[Option].cartesian[Int, Int, Int]) @@ -17,6 +17,9 @@ class OptionTests extends CatsSuite { checkAll("Option[Int] with Option", TraverseTests[Option].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Option]", SerializableTests.serializable(Traverse[Option])) + checkAll("Option with Unit", MonadErrorTests[Option, Unit].monadError[Int, Int, Int]) + checkAll("MonadError[Option, Unit]", SerializableTests.serializable(MonadError[Option, Unit])) + test("show") { none[Int].show should === ("None") 1.some.show should === ("Some(1)")