diff --git a/core/src/main/scala/cats/data/IndexedStateT.scala b/core/src/main/scala/cats/data/IndexedStateT.scala index 9f1f2a7c7f..0705bea46b 100644 --- a/core/src/main/scala/cats/data/IndexedStateT.scala +++ b/core/src/main/scala/cats/data/IndexedStateT.scala @@ -262,6 +262,16 @@ sealed abstract private[data] class IndexedStateTInstances extends IndexedStateT def defer[A](fa: => IndexedStateT[F, SA, SB, A]): IndexedStateT[F, SA, SB, A] = IndexedStateT.applyF(F.defer(fa.runF)) } + + implicit def catsDataFunctorFilterForIndexedStateT[F[_], SA, SB]( + implicit + ev1: Monad[F], + ev2: FunctorFilter[F] + ): FunctorFilter[IndexedStateT[F, SA, SB, ?]] = + new IndexedStateTFunctorFilter[F, SA, SB] { + val F0 = ev1 + val FF = ev2 + } } sealed abstract private[data] class IndexedStateTInstances1 extends IndexedStateTInstances2 { @@ -475,3 +485,15 @@ sealed abstract private[data] class IndexedStateTMonadError[F[_], S, E] def handleErrorWith[A](fa: IndexedStateT[F, S, S, A])(f: E => IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, A] = IndexedStateT(s => F.handleErrorWith(fa.run(s))(e => f(e).run(s))) } + +private[this] trait IndexedStateTFunctorFilter[F[_], SA, SB] extends FunctorFilter[IndexedStateT[F, SA, SB, ?]] { + + implicit def F0: Monad[F] + def FF: FunctorFilter[F] + + def functor: Functor[IndexedStateT[F, SA, SB, ?]] = + IndexedStateT.catsDataFunctorForIndexedStateT(FF.functor) + + def mapFilter[A, B](fa: IndexedStateT[F, SA, SB, A])(f: A => Option[B]): IndexedStateT[F, SA, SB, B] = + fa.flatMapF(a => FF.mapFilter(F0.pure(a))(f)) +} diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 612d0529c9..20f6eb0e16 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -168,6 +168,11 @@ sealed abstract private[data] class KleisliInstances extends KleisliInstances0 { } } } + + implicit def catsDataFunctorFilterForKleisli[F[_], A]( + implicit ev: FunctorFilter[F] + ): FunctorFilter[Kleisli[F, A, ?]] = + new KleisliFunctorFilter[F, A] { val FF = ev } } sealed abstract private[data] class KleisliInstances0 extends KleisliInstances0_5 { @@ -503,3 +508,15 @@ private trait KleisliDistributive[F[_], R] extends Distributive[Kleisli[F, R, ?] def map[A, B](fa: Kleisli[F, R, A])(f: A => B): Kleisli[F, R, B] = fa.map(f) } + +private[this] trait KleisliFunctorFilter[F[_], R] extends FunctorFilter[Kleisli[F, R, ?]] { + + def FF: FunctorFilter[F] + + def functor: Functor[Kleisli[F, R, ?]] = Kleisli.catsDataFunctorForKleisli(FF.functor) + + def mapFilter[A, B](fa: Kleisli[F, R, A])(f: A => Option[B]): Kleisli[F, R, B] = + Kleisli[F, R, B] { r => + FF.mapFilter(fa.run(r))(f) + } +} diff --git a/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala b/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala index 4fb6f69b5f..a26b4f4f4c 100644 --- a/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala +++ b/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala @@ -357,6 +357,18 @@ class IndexedStateTSuite extends CatsSuite { Functor[IndexedStateT[ListWrapper, String, Int, ?]] } + { + implicit val F0 = ListWrapper.monad + implicit val FF = ListWrapper.functorFilter + + checkAll("IndexedStateT[ListWrapper, String, Int, ?]", + FunctorFilterTests[IndexedStateT[ListWrapper, String, Int, ?]].functorFilter[Int, Int, Int]) + checkAll("FunctorFilter[IndexedStateT[ListWrapper, String, Int, ?]]", + SerializableTests.serializable(FunctorFilter[IndexedStateT[ListWrapper, String, Int, ?]])) + + FunctorFilter[IndexedStateT[ListWrapper, String, Int, ?]] + } + { implicit val F: Monad[ListWrapper] = ListWrapper.monad implicit val FS: Contravariant[IndexedStateT[ListWrapper, ?, Int, Int]] = diff --git a/tests/src/test/scala/cats/tests/KleisliSuite.scala b/tests/src/test/scala/cats/tests/KleisliSuite.scala index e9bf90e1b8..1397a7b60e 100644 --- a/tests/src/test/scala/cats/tests/KleisliSuite.scala +++ b/tests/src/test/scala/cats/tests/KleisliSuite.scala @@ -3,7 +3,7 @@ package tests import cats.Contravariant import cats.arrow._ -import cats.data.{Const, EitherT, Kleisli, Reader} +import cats.data.{Const, EitherT, Kleisli, Reader, ReaderT} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ @@ -141,6 +141,17 @@ class KleisliSuite extends CatsSuite { checkAll("Functor[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Functor[Kleisli[Option, Int, ?]])) } + { + implicit val FF = ListWrapper.functorFilter + + checkAll("Kleisli[ListWrapper, Int, ?]", + FunctorFilterTests[Kleisli[ListWrapper, Int, ?]].functorFilter[Int, Int, Int]) + checkAll("FunctorFilter[Kleisli[ListWrapper, Int, ?]]", + SerializableTests.serializable(FunctorFilter[Kleisli[ListWrapper, Int, ?]])) + + FunctorFilter[ReaderT[ListWrapper, Int, ?]] + } + { checkAll("Kleisli[Function0, Int, ?]", DistributiveTests[Kleisli[Function0, Int, ?]].distributive[Int, Int, Int, Option, Id])