-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Open
Description
I think it is possible to derive an instance of MonadError[M, E1]
from MonadError[M, E]
if we can prove that E1<~< E
. I tried to implement it and came up with:
implicit def liskovMonadError[M[_], E1, E](implicit ME: MonadError[M, E],
liskov: Liskov[E1, E],
E1: ClassTag[E1]): MonadError[M, E1] =
new MonadError[M, E1]{
override def raiseError[A](e1: E1) = ME.raiseError(liskov.coerce(e1))
override def handleErrorWith[A](fa: M[A])(f: E1 => M[A]) = ME.recoverWith(fa) {
case e1: E1 => f(e1)
}
override def pure[A](x: A) = ME.pure(x)
override def flatMap[A, B](fa: M[A])(f: A => M[B]) = ME.flatMap(fa)(f)
override def tailRecM[A, B](a: A)(f: A => M[Either[A, B]]) = ME.tailRecM(a)(f)
}
The "shady" part is handleErrorWith
where we need to check at runtime whether we have the right type (hence the ClassTag
). I thought it would not work, but it seems to pass all the MonadError
laws:
sealed trait Error
case class SubError1(s: String) extends Error
case class SubError2(s: String) extends Error
type F[A] = Either[Error, A]
implicit val ArbitraryError: Arbitrary[Error] = Arbitrary(
Arbitrary.arbitrary[String].flatMap(s => Gen.oneOf(SubError1(s), SubError2(s))))
implicit val ArbitrarySubError1: Arbitrary[SubError1] = Arbitrary(
Arbitrary.arbitrary[String].map(s => SubError1(s)))
implicit val CogenSubError1: Cogen[SubError1] = Cogen[String].contramap(_.s)
implicit val EqError: Eq[Error] = Eq.fromUniversalEquals[Error]
implicit val EqSubError1: Eq[SubError1] = Eq.fromUniversalEquals[SubError1]
checkAll("Liskov",
MonadErrorTests[F, SubError1](liskovMonadError)
.monadError[Int, String, Int])
Conversely, ApplicativeError
might have some kind of def widenError[E1](implicit ev: Liskov[E, E1]): ApplicativeError[F, E1]
.
lukasz-golebiewski
Metadata
Metadata
Assignees
Labels
No labels