Skip to content

Commit

Permalink
Add a MonoidK and SemigroupK instance for Kleisli.
Browse files Browse the repository at this point in the history
Combine the result of Kleislis using MonoidK[F] or SemigroupK[F].
  • Loading branch information
peterneyens committed Jun 8, 2016
1 parent a315749 commit e112955
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 13 deletions.
38 changes: 26 additions & 12 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 {
implicit val catsDataMonoidKForKleisliId: MonoidK[λ[α => Kleisli[Id, α, α]]] =
catsDataMonoidKForKleisli[Id]

implicit def catsDataMonoidKForKleisliAB[F[_], A](implicit M: MonoidK[F]): MonoidK[Kleisli[F, A, ?]] =
new KleisliABMonoidK[F, A] { def F: MonoidK[F] = M }

implicit def catsDataArrowForKleisli[F[_]](implicit ev: Monad[F]): Arrow[Kleisli[F, ?, ?]] =
new KleisliArrow[F] { def F: Monad[F] = ev }

Expand Down Expand Up @@ -152,6 +155,8 @@ private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1
implicit def catsDataSemigroupKForKleisli[F[_]](implicit ev: FlatMap[F]): SemigroupK[λ[α => Kleisli[F, α, α]]] =
new KleisliSemigroupK[F] { def F: FlatMap[F] = ev }

implicit def catsDataSemigroupKForKleisliAB[F[_], A](implicit S: SemigroupK[F]): SemigroupK[Kleisli[F, A, ?]] =
new KleisliABSemigroupK[F, A] { def F: SemigroupK[F] = S }
}

private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 {
Expand Down Expand Up @@ -181,7 +186,6 @@ private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4
}

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] =
Expand All @@ -197,7 +201,7 @@ private[data] sealed abstract class KleisliInstances4 {
}
}

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

def lift[A, B](f: A => B): Kleisli[F, A, B] =
Expand All @@ -213,7 +217,7 @@ private trait KleisliArrow[F[_]] extends Arrow[Kleisli[F, ?, ?]] with KleisliSpl
super[KleisliSplit].split(f, g)
}

private trait KleisliSplit[F[_]] extends Split[Kleisli[F, ?, ?]] {
private[data] trait KleisliSplit[F[_]] extends Split[Kleisli[F, ?, ?]] {
implicit def F: FlatMap[F]

def split[A, B, C, D](f: Kleisli[F, A, B], g: Kleisli[F, C, D]): Kleisli[F, (A, C), (B, D)] =
Expand All @@ -223,7 +227,7 @@ private trait KleisliSplit[F[_]] extends Split[Kleisli[F, ?, ?]] {
f.compose(g)
}

private trait KleisliStrong[F[_]] extends Strong[Kleisli[F, ?, ?]] {
private[data] trait KleisliStrong[F[_]] extends Strong[Kleisli[F, ?, ?]] {
implicit def F: Functor[F]

override def lmap[A, B, C](fab: Kleisli[F, A, B])(f: C => A): Kleisli[F, C, B] =
Expand All @@ -242,33 +246,45 @@ private trait KleisliStrong[F[_]] extends Strong[Kleisli[F, ?, ?]] {
fa.second[C]
}

private trait KleisliSemigroup[F[_], A, B] extends Semigroup[Kleisli[F, A, B]] {
private[data] trait KleisliSemigroup[F[_], A, B] extends Semigroup[Kleisli[F, A, B]] {
implicit def FB: Semigroup[F[B]]

override def combine(a: Kleisli[F, A, B], b: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli[F, A, B](x => FB.combine(a.run(x), b.run(x)))
}

private trait KleisliMonoid[F[_], A, B] extends Monoid[Kleisli[F, A, B]] with KleisliSemigroup[F, A, B] {
private[data] trait KleisliMonoid[F[_], A, B] extends Monoid[Kleisli[F, A, B]] with KleisliSemigroup[F, A, B] {
implicit def FB: Monoid[F[B]]

override def empty: Kleisli[F, A, B] = Kleisli[F, A, B](a => FB.empty)
}

private trait KleisliSemigroupK[F[_]] extends SemigroupK[λ[α => Kleisli[F, α, α]]] {
private[data] trait KleisliSemigroupK[F[_]] extends SemigroupK[λ[α => Kleisli[F, α, α]]] {
implicit def F: FlatMap[F]

override def combineK[A](a: Kleisli[F, A, A], b: Kleisli[F, A, A]): Kleisli[F, A, A] = a compose b
}

private trait KleisliMonoidK[F[_]] extends MonoidK[λ[α => Kleisli[F, α, α]]] with KleisliSemigroupK[F] {
private[data] trait KleisliMonoidK[F[_]] extends MonoidK[λ[α => Kleisli[F, α, α]]] with KleisliSemigroupK[F] {
implicit def F: Monad[F]

override def empty[A]: Kleisli[F, A, A] = Kleisli(F.pure[A])
}

private[data] sealed trait KleisliABSemigroupK[F[_], A] extends SemigroupK[Kleisli[F, A, ?]] {
implicit def F: SemigroupK[F]

override def combineK[B](x: Kleisli[F, A, B], y: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli(a => F.combineK(x.run(a), y.run(a)))
}

private[data] sealed trait KleisliABMonoidK[F[_], A] extends MonoidK[Kleisli[F, A, ?]] with KleisliABSemigroupK[F, A] {
implicit def F: MonoidK[F]

private trait KleisliApplicativeError[F[_], A, E] extends KleisliApplicative[F, A] with ApplicativeError[Kleisli[F, A, ?], E] {
override def empty[B]: Kleisli[F, A, B] = Kleisli(_ => F.empty[B])
}

private[data] 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]
Expand All @@ -280,11 +296,9 @@ private trait KleisliApplicativeError[F[_], A, E] extends KleisliApplicative[F,
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, ?]] {
private[data] trait KleisliApplicative[F[_], A] extends Applicative[Kleisli[F, A, ?]] {
implicit def F: Applicative[F]

def pure[B](x: B): Kleisli[F, A, B] =
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/std/function.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private[std] sealed trait Function1Instances extends Function1Instances0 {
def compose[A, B, C](f: B => C, g: A => B): A => C = f.compose(g)
}

implicit def catsStdMonoidForFunction1[A,B](implicit M: Monoid[B]): Monoid[A => B] =
implicit def catsStdMonoidForFunction1[A, B](implicit M: Monoid[B]): Monoid[A => B] =
new Function1Monoid[A, B] { def B: Monoid[B] = M }

implicit val catsStdMonoidKForFunction1: MonoidK[λ[α => α => α]] =
Expand Down
12 changes: 12 additions & 0 deletions tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ class KleisliTests extends CatsSuite {
checkAll("SemigroupK[λ[α => Kleisli[Option, α, α]]]", SerializableTests.serializable(catsDataSemigroupKForKleisli))
}

{
implicit val catsDataSemigroupKForKleisliAB = Kleisli.catsDataSemigroupKForKleisliAB[Option, String]
checkAll("Kleisli[Option, String, Int]", SemigroupKTests[Kleisli[Option, String, ?]].semigroupK[Int])
checkAll("SemigroupK[Kleisli[Option, String, ?]]", SerializableTests.serializable(catsDataSemigroupKForKleisliAB))
}

{
implicit val catsDataMonoidKForKleisliAB = Kleisli.catsDataMonoidKForKleisliAB[Option, String]
checkAll("Kleisli[Option, String, Int]", MonoidKTests[Kleisli[Option, String, ?]].monoidK[Int])
checkAll("MonoidK[Kleisli[Option, String, ?]]", SerializableTests.serializable(catsDataMonoidKForKleisliAB))
}

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

0 comments on commit e112955

Please sign in to comment.