Laws for traverse and foldable operations that work with applicative effects, enabling structure-preserving transformations.
import cats.laws._
import cats.laws.discipline._
import cats.Traverse
import cats.Foldable
import cats.ReducibleLaws for structures that can be folded down to a summary value.
trait FoldableLaws[F[_]] {
implicit def F: Foldable[F]
def leftFoldConsistentWithFoldMap[A, B](fa: F[A], f: A => B)(implicit M: Monoid[B]): IsEq[B]
def rightFoldConsistentWithFoldMap[A, B](fa: F[A], f: A => B)(implicit M: Monoid[B]): IsEq[B]
def existsConsistentWithFind[A](fa: F[A], p: A => Boolean): IsEq[Boolean]
def forallConsistentWithExists[A](fa: F[A], p: A => Boolean): IsEq[Boolean]
def forallReturnsTrueIfEmpty[A](fa: F[A], p: A => Boolean): IsEq[Boolean]
def foldMIdIsFoldL[A](fa: F[A])(implicit A: Monoid[A]): IsEq[A]
def foldMapMIdIsFoldM[A, B](fa: F[A], f: A => B)(implicit B: Monoid[B]): IsEq[B]
}
object FoldableLaws {
def apply[F[_]](implicit ev: Foldable[F]): FoldableLaws[F]
}trait FoldableTests[F[_]] {
def laws: FoldableLaws[F]
def foldable[A: Arbitrary, B: Arbitrary](implicit
ArbFA: Arbitrary[F[A]],
A: Monoid[A],
B: Monoid[B],
CogenA: Cogen[A],
EqA: Eq[A],
EqB: Eq[B],
EqOptionA: Eq[Option[A]]
): RuleSet
}
object FoldableTests {
def apply[F[_]: Foldable]: FoldableTests[F]
}Laws for structures that can be traversed with applicative effects.
trait TraverseLaws[F[_]] extends FunctorLaws[F] with FoldableLaws[F] {
implicit override def F: Traverse[F]
def traverseIdentity[A, B](fa: F[A], f: A => B): IsEq[F[B]]
def traverseSequentialComposition[A, B, C, M[_], N[_]](
fa: F[A],
f: A => M[B],
g: B => N[C]
)(implicit
MN: Applicative[M],
NN: Applicative[N],
MN: ComposeApplicative[M, N]
): IsEq[M[N[F[C]]]]
def traverseParallelComposition[A, B, M[_], N[_]](
fa: F[A],
f: A => M[B],
g: A => N[B]
)(implicit
MN: Applicative[M],
NN: Applicative[N],
MN: ProductApplicative[M, N]
): IsEq[(M[F[B]], N[F[B]])]
def sequenceConsistent[A, M[_]](fma: F[M[A]])(implicit M: Applicative[M]): IsEq[M[F[A]]]
def naturalTransformation[A, M[_], N[_]](
fma: F[M[A]],
nat: M ~> N
)(implicit M: Applicative[M], N: Applicative[N]): IsEq[N[F[A]]]
}
object TraverseLaws {
def apply[F[_]](implicit ev: Traverse[F]): TraverseLaws[F]
}trait TraverseTests[F[_]] extends FunctorTests[F] with FoldableTests[F] {
def laws: TraverseLaws[F]
def traverse[A: Arbitrary, B: Arbitrary, C: Arbitrary, M: Applicative, N: Applicative](implicit
ArbFA: Arbitrary[F[A]],
ArbFB: Arbitrary[F[B]],
ArbFC: Arbitrary[F[C]],
ArbMA: Arbitrary[M[A]],
ArbMB: Arbitrary[M[B]],
ArbMC: Arbitrary[M[C]],
ArbFMA: Arbitrary[F[M[A]]],
CogenA: Cogen[A],
CogenB: Cogen[B],
CogenC: Cogen[C],
EqFA: Eq[F[A]],
EqFC: Eq[F[C]],
EqM: Eq[M[Unit]],
EqMFA: Eq[M[F[A]]],
EqMFB: Eq[M[F[B]]],
EqMFC: Eq[M[F[C]]],
EqMFABC: Eq[M[F[((A, B), C)]]],
iso: Isomorphisms[F]
): RuleSet
}
object TraverseTests {
def apply[F[_]: Traverse]: TraverseTests[F]
}Laws for non-empty structures that can be reduced to a single value.
trait ReducibleLaws[F[_]] extends FoldableLaws[F] {
implicit override def F: Reducible[F]
def reduceLeftToConsistentWithReduceLeftOption[A, B](
fa: F[A],
f: A => B,
g: (B, A) => B
): IsEq[B]
def reduceRightToConsistentWithReduceRightOption[A, B](
fa: F[A],
f: A => B,
g: (A, Eval[B]) => Eval[B]
): IsEq[B]
def reduceReduceLeftConsistent[A](fa: F[A])(implicit A: Semigroup[A]): IsEq[A]
def reduceMapReduceConsistent[A, B](fa: F[A], f: A => B)(implicit B: Semigroup[B]): IsEq[B]
}
object ReducibleLaws {
def apply[F[_]](implicit ev: Reducible[F]): ReducibleLaws[F]
}trait ReducibleTests[F[_]] extends FoldableTests[F] {
def laws: ReducibleLaws[F]
def reducible[A: Arbitrary, B: Arbitrary](implicit
ArbFA: Arbitrary[F[A]],
A: Semigroup[A],
B: Semigroup[B],
CogenA: Cogen[A],
EqA: Eq[A],
EqB: Eq[B]
): RuleSet
}
object ReducibleTests {
def apply[F[_]: Reducible]: ReducibleTests[F]
}Laws for structures that can be folded without considering order.
trait UnorderedFoldableLaws[F[_]] {
implicit def F: UnorderedFoldable[F]
def unorderedFoldConsistentWithUnorderedFoldMap[A: CommutativeMonoid](fa: F[A]): IsEq[A]
def isEmptyConsistentWithEmpty[A](fa: F[A]): IsEq[Boolean]
def nonEmptyConsistentWithEmpty[A](fa: F[A]): IsEq[Boolean]
}
object UnorderedFoldableLaws {
def apply[F[_]](implicit ev: UnorderedFoldable[F]): UnorderedFoldableLaws[F]
}trait UnorderedFoldableTests[F[_]] {
def laws: UnorderedFoldableLaws[F]
def unorderedFoldable[A: Arbitrary](implicit
ArbFA: Arbitrary[F[A]],
A: CommutativeMonoid[A],
EqA: Eq[A]
): RuleSet
}
object UnorderedFoldableTests {
def apply[F[_]: UnorderedFoldable]: UnorderedFoldableTests[F]
}Laws for structures that can be traversed without considering order.
trait UnorderedTraverseLaws[F[_]] extends UnorderedFoldableLaws[F] {
implicit override def F: UnorderedTraverse[F]
def unorderedTraverseIdentity[A, B](fa: F[A], f: A => B): IsEq[F[B]]
def unorderedSequenceConsistent[A, G[_]: CommutativeApplicative](fga: F[G[A]]): IsEq[G[F[A]]]
}
object UnorderedTraverseLaws {
def apply[F[_]](implicit ev: UnorderedTraverse[F]): UnorderedTraverseLaws[F]
}trait UnorderedTraverseTests[F[_]] extends UnorderedFoldableTests[F] {
def laws: UnorderedTraverseLaws[F]
def unorderedTraverse[A: Arbitrary, B: Arbitrary, C: Arbitrary, M: CommutativeApplicative](implicit
ArbFA: Arbitrary[F[A]],
ArbFMA: Arbitrary[F[M[A]]],
CogenA: Cogen[A],
CogenB: Cogen[B],
EqFA: Eq[F[A]],
EqFC: Eq[F[C]],
EqMFA: Eq[M[F[A]]],
EqMFB: Eq[M[F[B]]]
): RuleSet
}
object UnorderedTraverseTests {
def apply[F[_]: UnorderedTraverse]: UnorderedTraverseTests[F]
}Laws for non-empty structures that can be traversed.
trait NonEmptyTraverseLaws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] {
implicit override def F: NonEmptyTraverse[F]
def nonEmptyTraverseIdentity[A, B](fa: F[A], f: A => B): IsEq[F[B]]
def nonEmptySequenceConsistent[A, G[_]: Apply](fga: F[G[A]]): IsEq[G[F[A]]]
}
object NonEmptyTraverseLaws {
def apply[F[_]](implicit ev: NonEmptyTraverse[F]): NonEmptyTraverseLaws[F]
}trait NonEmptyTraverseTests[F[_]] extends TraverseTests[F] with ReducibleTests[F] {
def laws: NonEmptyTraverseLaws[F]
def nonEmptyTraverse[A: Arbitrary, B: Arbitrary, C: Arbitrary, M: Apply, N: Apply](implicit
ArbFA: Arbitrary[F[A]],
ArbFB: Arbitrary[F[B]],
ArbFC: Arbitrary[F[C]],
ArbMA: Arbitrary[M[A]],
ArbMB: Arbitrary[M[B]],
ArbMC: Arbitrary[M[C]],
ArbFMA: Arbitrary[F[M[A]]],
CogenA: Cogen[A],
CogenB: Cogen[B],
CogenC: Cogen[C],
EqFA: Eq[F[A]],
EqFC: Eq[F[C]],
EqMFA: Eq[M[F[A]]],
EqMFB: Eq[M[F[B]]],
EqMFC: Eq[M[F[C]]],
iso: Isomorphisms[F]
): RuleSet
}
object NonEmptyTraverseTests {
def apply[F[_]: NonEmptyTraverse]: NonEmptyTraverseTests[F]
}import cats.laws.discipline.TraverseTests
import cats.syntax.all._
class ListTraverseTest extends AnyFunSuite with Checkers {
checkAll("List.TraverseLaws", TraverseTests[List].traverse[Int, String, Double, Option, Option])
}import cats.laws.discipline.ReducibleTests
import cats.data.NonEmptyList
class NonEmptyListReducibleTest extends AnyFunSuite with Checkers {
checkAll("NonEmptyList.ReducibleLaws", ReducibleTests[NonEmptyList].reducible[Int, String])
}import cats.laws.TraverseLaws
import cats.syntax.all._
val laws = TraverseLaws[List]
// Verify traverse identity law
val identityLaw = laws.traverseIdentity(List(1, 2, 3), (x: Int) => x * 2)
assert(identityLaw.lhs == identityLaw.rhs)
// Verify sequence is consistent with traverse
val seqLaw = laws.sequenceConsistent(List(Some(1), Some(2), None))
assert(seqLaw.lhs == seqLaw.rhs)