Skip to content

Commit

Permalink
Merge pull request #1029 from kailuowang/applicativeErrorKleisli
Browse files Browse the repository at this point in the history
Added an `ApplicativeError` instance for `Kleisli` and a `MonadError[Option, Unit]` to `std.option`
  • Loading branch information
adelbertc committed May 13, 2016
2 parents 15fc946 + c52c6ec commit 53dc641
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 16 deletions.
51 changes: 39 additions & 12 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
}

Expand Down Expand Up @@ -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)))
}
8 changes: 6 additions & 2 deletions core/src/main/scala/cats/std/option.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down
7 changes: 6 additions & 1 deletion tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand All @@ -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, ?]]))

Expand Down
5 changes: 4 additions & 1 deletion tests/src/test/scala/cats/tests/OptionTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand All @@ -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)")
Expand Down

0 comments on commit 53dc641

Please sign in to comment.