From 793f2ccfa3b676582a88a9ac7b67666032cae2c4 Mon Sep 17 00:00:00 2001 From: Rutvik Patel Date: Mon, 18 Jun 2018 16:04:35 +0530 Subject: [PATCH 1/3] added MonadError instance for OptionT where F is a Monad --- core/src/main/scala/cats/data/OptionT.scala | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 74fb633fb5..3fffc63d0a 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -234,6 +234,9 @@ private[data] sealed abstract class OptionTInstances0 extends OptionTInstances1 implicit def catsDataMonadErrorForOptionT[F[_], E](implicit F0: MonadError[F, E]): MonadError[OptionT[F, ?], E] = new OptionTMonadError[F, E] { implicit val F = F0 } + implicit def catsDataMonadErrorMonadForOptionT[F[_]](implicit F0: Monad[F]): MonadError[OptionT[F, ?], Unit] = + new OptionTMonadErrorMonad[F] { implicit val F = F0 } + implicit def catsDataContravariantMonoidalForOptionT[F[_]](implicit F0: ContravariantMonoidal[F]): ContravariantMonoidal[OptionT[F, ?]] = new OptionTContravariantMonoidal[F] { implicit val F = F0 } @@ -287,6 +290,29 @@ private[data] trait OptionTMonad[F[_]] extends Monad[OptionT[F, ?]] { ))) } +private[data] trait OptionTMonadErrorMonad[F[_]] extends MonadError[OptionT[F, ?], Unit] { + implicit def F: Monad[F] + + def pure[A](a: A): OptionT[F, A] = OptionT.pure(a) + + def flatMap[A, B](fa: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] = fa.flatMap(f) + + override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = fa.map(f) + + def tailRecM[A, B](a: A)(f: A => OptionT[F, Either[A, B]]): OptionT[F, B] = + OptionT(F.tailRecM(a)(a0 => F.map(f(a0).value)( + _.fold(Either.right[A, Option[B]](None))(_.map(b => Some(b): Option[B])) + ))) + + def raiseError[A](e: Unit): OptionT[F, A] = OptionT.none + + def handleErrorWith[A](fa: OptionT[F, A])(f: Unit => OptionT[F, A]): OptionT[F, A] = + OptionT(F.flatMap(fa.value) { + case s @ Some(_) => F.pure(s) + case None => f(()).value + }) +} + private trait OptionTMonadError[F[_], E] extends MonadError[OptionT[F, ?], E] with OptionTMonad[F] { override def F: MonadError[F, E] From a46f8893609e0d4b503366d32d96ae46ec8885ff Mon Sep 17 00:00:00 2001 From: Rutvik Patel Date: Mon, 18 Jun 2018 16:53:19 +0530 Subject: [PATCH 2/3] test added --- core/src/main/scala/cats/data/OptionT.scala | 6 +++--- tests/src/test/scala/cats/tests/OptionTSuite.scala | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 3fffc63d0a..bceef02265 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -234,9 +234,6 @@ private[data] sealed abstract class OptionTInstances0 extends OptionTInstances1 implicit def catsDataMonadErrorForOptionT[F[_], E](implicit F0: MonadError[F, E]): MonadError[OptionT[F, ?], E] = new OptionTMonadError[F, E] { implicit val F = F0 } - implicit def catsDataMonadErrorMonadForOptionT[F[_]](implicit F0: Monad[F]): MonadError[OptionT[F, ?], Unit] = - new OptionTMonadErrorMonad[F] { implicit val F = F0 } - implicit def catsDataContravariantMonoidalForOptionT[F[_]](implicit F0: ContravariantMonoidal[F]): ContravariantMonoidal[OptionT[F, ?]] = new OptionTContravariantMonoidal[F] { implicit val F = F0 } @@ -257,6 +254,9 @@ private[data] sealed abstract class OptionTInstances1 extends OptionTInstances2 implicit def catsDataEqForOptionT[F[_], A](implicit F0: Eq[F[Option[A]]]): Eq[OptionT[F, A]] = new OptionTEq[F, A] { implicit val F = F0 } + + implicit def catsDataMonadErrorMonadForOptionT[F[_]](implicit F0: Monad[F]): MonadError[OptionT[F, ?], Unit] = + new OptionTMonadErrorMonad[F] { implicit val F = F0 } } private[data] sealed abstract class OptionTInstances2 extends OptionTInstances3 { diff --git a/tests/src/test/scala/cats/tests/OptionTSuite.scala b/tests/src/test/scala/cats/tests/OptionTSuite.scala index 32e3458fda..8ba0935b61 100644 --- a/tests/src/test/scala/cats/tests/OptionTSuite.scala +++ b/tests/src/test/scala/cats/tests/OptionTSuite.scala @@ -130,6 +130,13 @@ class OptionTSuite extends CatsSuite { checkAll("Semigroup[OptionT[ListWrapper, Int]]", SerializableTests.serializable(Semigroup[OptionT[ListWrapper, Int]])) } + { + // MonadError instance where F has a Monad + implicit val F = ListWrapper.monad + checkAll("OptionT[ListWrapper, Int]", MonadErrorTests[OptionT[ListWrapper, ?], Unit].monadError[Int, Int, Int]) + checkAll("MonadError[OptionT[List, ?]]", SerializableTests.serializable(MonadError[OptionT[ListWrapper, ?], Unit])) + } + test("fold and cata consistent") { forAll { (o: OptionT[List, Int], s: String, f: Int => String) => o.fold(s)(f) should === (o.cata(s, f)) From 50cd9e904544efa132a4e0626c0b5c26ac6d3e5c Mon Sep 17 00:00:00 2001 From: Rutvik Patel Date: Mon, 18 Jun 2018 18:48:50 +0530 Subject: [PATCH 3/3] removed duplicate code using OptionTMonad[F] --- core/src/main/scala/cats/data/OptionT.scala | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index bceef02265..30976b1a59 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -290,23 +290,12 @@ private[data] trait OptionTMonad[F[_]] extends Monad[OptionT[F, ?]] { ))) } -private[data] trait OptionTMonadErrorMonad[F[_]] extends MonadError[OptionT[F, ?], Unit] { +private[data] trait OptionTMonadErrorMonad[F[_]] extends MonadError[OptionT[F, ?], Unit] with OptionTMonad[F] { implicit def F: Monad[F] - def pure[A](a: A): OptionT[F, A] = OptionT.pure(a) - - def flatMap[A, B](fa: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] = fa.flatMap(f) - - override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = fa.map(f) - - def tailRecM[A, B](a: A)(f: A => OptionT[F, Either[A, B]]): OptionT[F, B] = - OptionT(F.tailRecM(a)(a0 => F.map(f(a0).value)( - _.fold(Either.right[A, Option[B]](None))(_.map(b => Some(b): Option[B])) - ))) - - def raiseError[A](e: Unit): OptionT[F, A] = OptionT.none + override def raiseError[A](e: Unit): OptionT[F, A] = OptionT.none - def handleErrorWith[A](fa: OptionT[F, A])(f: Unit => OptionT[F, A]): OptionT[F, A] = + override def handleErrorWith[A](fa: OptionT[F, A])(f: Unit => OptionT[F, A]): OptionT[F, A] = OptionT(F.flatMap(fa.value) { case s @ Some(_) => F.pure(s) case None => f(()).value