MonadError with Liskov
Nobody has claimed this yet.
- Dominant language
- Scala
- Stars
- 5.5k
- Forks
- 1.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 5
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] .
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the proposed liskovMonadError implementation and the MonadError law-checking example in the issue. Investigate the existing MonadError and ApplicativeError APIs and law tests, then determine what API or test outcome would count as done; no repository file or test path is named in the payload.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- scala
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100