Laws for testing type class instances in the Cats functional programming library for Scala
Laws for error handling with ApplicativeError and MonadError, providing principled error recovery and handling.
import cats.laws._
import cats.laws.discipline._
import cats.ApplicativeError
import cats.MonadErrorLaws for applicative functors that can handle and recover from errors.
trait ApplicativeErrorLaws[F[_], E] extends ApplicativeLaws[F] {
implicit override def F: ApplicativeError[F, E]
def applicativeErrorHandleWith[A](e: E, f: E => F[A]): IsEq[F[A]]
def applicativeErrorHandle[A](e: E, f: E => A): IsEq[F[A]]
def handleErrorWithPure[A](a: A, f: E => F[A]): IsEq[F[A]]
def handleErrorPure[A](a: A, f: E => A): IsEq[F[A]]
def raiseErrorAttempt(e: E): IsEq[F[Either[E, Unit]]]
def pureAttempt[A](a: A): IsEq[F[Either[E, A]]]
def handleErrorWithConsistentWithRecoverWith[A](fa: F[A], f: E => F[A]): IsEq[F[A]]
def handleErrorConsistentWithRecover[A](fa: F[A], f: E => A): IsEq[F[A]]
def recoverConsistentWithRecoverWith[A](fa: F[A], pf: PartialFunction[E, A]): IsEq[F[A]]
def attemptConsistentWithAttemptT[A](fa: F[A]): IsEq[EitherT[F, E, A]]
def attemptFromEitherConsistentWithPure[A](eab: Either[E, A]): IsEq[F[Either[E, A]]]
def voidErrorConsistentWithHandleError(fu: F[Unit]): IsEq[F[Unit]]
def onErrorPure[A](a: A, f: E => F[Unit]): IsEq[F[A]]
def onErrorRaise[A](fa: F[A], e: E, fb: F[Unit]): IsEq[F[A]]
def adaptErrorPure[A](a: A, f: E => E): IsEq[F[A]]
def adaptErrorRaise[A](e: E, f: E => E): IsEq[F[A]]
def redeemDerivedFromAttemptMap[A, B](fa: F[A], fe: E => B, fs: A => B): IsEq[F[B]]
def raiseErrorDistributesOverApLeft[A](h: E => F[A], e: E): IsEq[F[A]]
def raiseErrorDistributesOverApRight[A](h: E => F[A], e: E): IsEq[F[A]]
}
object ApplicativeErrorLaws {
def apply[F[_], E](implicit ev: ApplicativeError[F, E]): ApplicativeErrorLaws[F, E]
}trait ApplicativeErrorTests[F[_], E] extends ApplicativeTests[F] {
def laws: ApplicativeErrorLaws[F, E]
def applicativeError[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit
ArbFA: Arbitrary[F[A]],
ArbFB: Arbitrary[F[B]],
ArbFC: Arbitrary[F[C]],
ArbFAtoB: Arbitrary[F[A => B]],
ArbFBtoC: Arbitrary[F[B => C]],
ArbE: Arbitrary[E],
CogenA: Cogen[A],
CogenB: Cogen[B],
CogenC: Cogen[C],
CogenE: Cogen[E],
EqFA: Eq[F[A]],
EqFB: Eq[F[B]],
EqFC: Eq[F[C]],
EqE: Eq[E],
EqFEitherEA: Eq[F[Either[E, A]]],
EqFEitherEB: Eq[F[Either[E, B]]],
EqFEitherEC: Eq[F[Either[E, C]]],
EqFUnit: Eq[F[Unit]],
iso: Isomorphisms[F]
): RuleSet
}
object ApplicativeErrorTests {
def apply[F[_], E](implicit ev: ApplicativeError[F, E]): ApplicativeErrorTests[F, E]
}Laws for monads that can handle errors, extending both ApplicativeError and Monad.
trait MonadErrorLaws[F[_], E] extends ApplicativeErrorLaws[F, E] with MonadLaws[F] {
implicit override def F: MonadError[F, E]
def monadErrorLeftZero[A, B](e: E, f: A => F[B]): IsEq[F[B]]
def monadErrorEnsureConsistency[A](fa: F[A], e: E, p: A => Boolean): IsEq[F[A]]
def monadErrorEnsureOrConsistency[A](fa: F[A], e: A => E, p: A => Boolean): IsEq[F[A]]
def adaptErrorPure[A](a: A, f: E => E): IsEq[F[A]]
def adaptErrorRaise[A](e: E, f: E => E): IsEq[F[A]]
def rethrowAttempt[A](fa: F[A]): IsEq[F[A]]
def redeemWithDerivedFromAttemptFlatMap[A, B](fa: F[A], fe: E => F[B], fs: A => F[B]): IsEq[F[B]]
}
object MonadErrorLaws {
def apply[F[_], E](implicit ev: MonadError[F, E]): MonadErrorLaws[F, E]
}trait MonadErrorTests[F[_], E] extends ApplicativeErrorTests[F, E] with MonadTests[F] {
def laws: MonadErrorLaws[F, E]
def monadError[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit
ArbFA: Arbitrary[F[A]],
ArbFB: Arbitrary[F[B]],
ArbFC: Arbitrary[F[C]],
ArbFAtoB: Arbitrary[F[A => B]],
ArbFBtoC: Arbitrary[F[B => C]],
ArbE: Arbitrary[E],
CogenA: Cogen[A],
CogenB: Cogen[B],
CogenC: Cogen[C],
CogenE: Cogen[E],
EqFA: Eq[F[A]],
EqFB: Eq[F[B]],
EqFC: Eq[F[C]],
EqE: Eq[E],
EqFEitherEA: Eq[F[Either[E, A]]],
EqFEitherEB: Eq[F[Either[E, B]]],
EqFEitherEC: Eq[F[Either[E, C]]],
EqFUnit: Eq[F[Unit]],
EqFInt: Eq[F[Int]],
iso: Isomorphisms[F]
): RuleSet
}
object MonadErrorTests {
def apply[F[_], E](implicit ev: MonadError[F, E]): MonadErrorTests[F, E]
}import cats.laws.discipline.MonadErrorTests
import cats.syntax.all._
class EitherMonadErrorTest extends AnyFunSuite with Checkers {
checkAll("Either.MonadErrorLaws", MonadErrorTests[Either[String, *], String].monadError[Int, String, Double])
}import cats.laws.discipline.ApplicativeErrorTests
import scala.util.Try
class TryApplicativeErrorTest extends AnyFunSuite with Checkers {
checkAll("Try.ApplicativeErrorLaws", ApplicativeErrorTests[Try, Throwable].applicativeError[Int, String, Double])
}import cats.laws.MonadErrorLaws
import cats.syntax.all._
val laws = MonadErrorLaws[Either[String, *], String]
// Verify error handling
val handleLaw = laws.applicativeErrorHandle("error", (e: String) => e.length)
assert(handleLaw.lhs == handleLaw.rhs)
// Verify left zero law (error short-circuits)
val leftZeroLaw = laws.monadErrorLeftZero[Int, String](
"error",
(x: Int) => Right(x.toString)
)
assert(leftZeroLaw.lhs == leftZeroLaw.rhs)Install with Tessl CLI
npx tessl i tessl/maven-org-typelevel--cats-laws-native0-5-2-13