or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

alternative-laws.mdbifunctor-laws.mdcategory-laws.mdcommutative-laws.mdcomonad-laws.mdcore-laws.mderror-laws.mdindex.mdparallel-laws.mdtesting-utilities.mdtraversal-laws.md
tile.json

traversal-laws.mddocs/

Traversal Laws

Laws for traverse and foldable operations that work with applicative effects, enabling structure-preserving transformations.

Imports

import cats.laws._
import cats.laws.discipline._
import cats.Traverse
import cats.Foldable
import cats.Reducible

Foldable Laws

Laws 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]
}

FoldableTests

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]
}

Traverse Laws

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]
}

TraverseTests

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]
}

Reducible Laws

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]
}

ReducibleTests

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]
}

UnorderedFoldable Laws

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]
}

UnorderedFoldableTests

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]
}

UnorderedTraverse Laws

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]
}

UnorderedTraverseTests

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]
}

NonEmptyTraverse Laws

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]
}

NonEmptyTraverseTests

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]
}

Usage Examples

Testing List as Traverse

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])
}

Testing NonEmptyList as Reducible

import cats.laws.discipline.ReducibleTests
import cats.data.NonEmptyList

class NonEmptyListReducibleTest extends AnyFunSuite with Checkers {
  checkAll("NonEmptyList.ReducibleLaws", ReducibleTests[NonEmptyList].reducible[Int, String])
}

Custom Traverse Law Verification

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)