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

Adding MonadRec for Kleisli #1279

Closed
wants to merge 3 commits into from
Closed
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
47 changes: 35 additions & 12 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,20 +180,20 @@ private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4
}
}

private[data] sealed abstract class KleisliInstances4 {
private[data] sealed abstract class KleisliInstances4 extends KleisliInstances5 {

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)
implicit def catsDataMonadReaderForKleisli[F[_], A](implicit M: Monad[F]): MonadReader[Kleisli[F, A, ?], A] =
new KleisliMonadReader[F, A] {
implicit def F: Monad[F] = M
}
}

val ask: Kleisli[F, A, A] = Kleisli(Monad[F].pure)
private[data] sealed abstract class KleisliInstances5 {

def local[B](f: A => A)(fa: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli(f.andThen(fa.run))
implicit def catsDataMonadRecForKleisli[F[_], A](implicit M: MonadRec[F]): MonadRec[Kleisli[F, A, ?]] =
new KleisliMonadRec[F, A] {
implicit def F: Monad[F] = M
implicit def RF: MonadRec[F] = M
}
}

Expand Down Expand Up @@ -283,7 +283,6 @@ private trait KleisliApplicativeError[F[_], A, E] extends KleisliApplicative[F,

}


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

Expand All @@ -299,3 +298,27 @@ private trait KleisliApplicative[F[_], A] extends Applicative[Kleisli[F, A, ?]]
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)))
}

private trait KleisliMonad[F[_], A] extends Monad[Kleisli[F, A, ?]] {
implicit def F: Monad[F]

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)
}

private trait KleisliMonadReader[F[_], A] extends MonadReader[Kleisli[F, A, ?], A] with KleisliMonad[F, A] {
val ask: Kleisli[F, A, A] = Kleisli(F.pure)

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

private trait KleisliMonadRec[F[_], A] extends MonadRec[Kleisli[F, A, ?]] with KleisliMonad[F, A] {
implicit def RF: MonadRec[F]

def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, B Xor C]): Kleisli[F, A, C] =
Kleisli[F, A, C](a => RF.tailRecM[B, C](b)(bb => f(bb).run(a)))
}
20 changes: 19 additions & 1 deletion tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ class KleisliTests extends CatsSuite {
checkAll("SemigroupK[λ[α => Kleisli[Option, α, α]]]", SerializableTests.serializable(catsDataSemigroupKForKleisli))
}


{
// F has a MonadRec
implicit val F = MonadRec[Option]

checkAll("Kleisli[Option, Int, Int]", MonadRecTests[Kleisli[Option, Int, ?]].monadRec[Int, Int, Int])
checkAll("MonadRec[Kleisli[Option, Int, ?]]", SerializableTests.serializable(MonadRec[Kleisli[Option, Int, ?]]))

Monad[Kleisli[Option, Int, ?]]
FlatMap[Kleisli[Option, Int, ?]]
Applicative[Kleisli[Option, Int, ?]]
Apply[Kleisli[Option, Int, ?]]
Functor[Kleisli[Option, Int, ?]]
MonadReader[Kleisli[Option, Int, ?], Int]
}

checkAll("Kleisli[Option, ?, Int]", ContravariantTests[Kleisli[Option, ?, Int]].contravariant[Int, Int, Int])
checkAll("Contravariant[Kleisli[Option, ?, Int]]", SerializableTests.serializable(Contravariant[Kleisli[Option, ?, Int]]))

Expand Down Expand Up @@ -163,6 +179,7 @@ class KleisliTests extends CatsSuite {
FlatMap[Kleisli[List, Int, ?]]
Semigroup[Kleisli[List, Int, String]]
SemigroupK[λ[α => Kleisli[List, α, α]]]
MonadRec[Kleisli[List, Int, ?]]

// F is Id
Functor[Kleisli[Id, Int, ?]]
Expand Down Expand Up @@ -206,5 +223,6 @@ class KleisliTests extends CatsSuite {
Monoid[IntReader[String]]
FlatMap[IntReader]
Semigroup[IntReader[String]]
MonadRec[IntReader]
}
}
}