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

parallel-laws.mddocs/

Parallel and Align Laws

Laws for parallel operations and alignment of functors, enabling concurrent execution and data alignment.

Imports

import cats.laws._
import cats.laws.discipline._
import cats.Parallel
import cats.Align
import cats.data.Ior

Parallel Laws

trait ParallelLaws[M[_], F[_]] {
  def parallel: Parallel.Aux[M, F]
  
  def parallelRoundTrip[A](ma: M[A]): IsEq[M[A]]
  def sequentialRoundTrip[A](fa: F[A]): IsEq[F[A]]
  def isomorphicPure[A](a: A): IsEq[M[A]]
  def isomorphicProduct[A, B](fa: F[A], fb: F[B]): IsEq[M[(A, B)]]
  def sequentialConsistentWithPureProduct[A, B](a: A, b: B): IsEq[M[(A, B)]]
}

object ParallelLaws {
  def apply[M[_], F[_]](implicit ev: Parallel[M, F]): ParallelLaws[M, F]
}

ParallelTests

trait ParallelTests[M[_], F[_]] {
  def laws: ParallelLaws[M, F]
  
  def parallel[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit
    ArbMA: Arbitrary[M[A]],
    ArbMB: Arbitrary[M[B]],
    ArbMC: Arbitrary[M[C]],
    ArbFA: Arbitrary[F[A]],
    ArbFB: Arbitrary[F[B]],
    ArbFC: Arbitrary[F[C]],
    CogenA: Cogen[A],
    CogenB: Cogen[B],
    CogenC: Cogen[C],
    EqMA: Eq[M[A]],
    EqMB: Eq[M[B]],
    EqMC: Eq[M[C]],
    EqMAB: Eq[M[(A, B)]],
    EqMABC: Eq[M[(A, B, C)]],
    EqFA: Eq[F[A]],
    EqFB: Eq[F[B]],
    EqFC: Eq[F[C]]
  ): RuleSet
}

object ParallelTests {
  def apply[M[_], F[_]](implicit ev: Parallel[M, F]): ParallelTests[M, F]
}

NonEmptyParallel Laws

trait NonEmptyParallelLaws[M[_], F[_]] {
  def nonEmptyParallel: NonEmptyParallel.Aux[M, F]
  
  def nonEmptyParallelRoundTrip[A](ma: M[A]): IsEq[M[A]]
  def nonEmptySequentialRoundTrip[A](fa: F[A]): IsEq[F[A]]
  def isomorphicMap2[A, B, C](fa: F[A], fb: F[B], f: (A, B) => C): IsEq[M[C]]
  def isomorphicAp[A, B](ff: F[A => B], fa: F[A]): IsEq[M[B]]
}

object NonEmptyParallelLaws {
  def apply[M[_], F[_]](implicit ev: NonEmptyParallel[M, F]): NonEmptyParallelLaws[M, F]
}

NonEmptyParallelTests

trait NonEmptyParallelTests[M[_], F[_]] {
  def laws: NonEmptyParallelLaws[M, F]
  
  def nonEmptyParallel[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit
    ArbMA: Arbitrary[M[A]],
    ArbMB: Arbitrary[M[B]],
    ArbMC: Arbitrary[M[C]],
    ArbFA: Arbitrary[F[A]],
    ArbFB: Arbitrary[F[B]],
    ArbFC: Arbitrary[F[C]],
    ArbFAtoB: Arbitrary[F[A => B]],
    CogenA: Cogen[A],
    CogenB: Cogen[B],
    CogenC: Cogen[C],
    EqMA: Eq[M[A]],
    EqMB: Eq[M[B]],
    EqMC: Eq[M[C]],
    EqFA: Eq[F[A]],
    EqFB: Eq[F[B]],
    EqFC: Eq[F[C]]
  ): RuleSet
}

object NonEmptyParallelTests {
  def apply[M[_], F[_]](implicit ev: NonEmptyParallel[M, F]): NonEmptyParallelTests[M, F]
}

Align Laws

trait AlignLaws[F[_]] extends FunctorLaws[F] {
  implicit override def F: Align[F]
  
  def alignAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): IsEq[F[Ior[Ior[A, B], C]]]
  def alignHomomorphism[A, B, C, D](fa: F[A], fb: F[B], f: A => C, g: B => D): IsEq[F[Ior[C, D]]]
  def alignWithConsistent[A, B, C](fa: F[A], fb: F[B], f: Ior[A, B] => C): IsEq[F[C]]
  def alignMergeWithConsistent[A](fa: F[A], fb: F[A]): IsEq[F[A]]
}

object AlignLaws {
  def apply[F[_]](implicit ev: Align[F]): AlignLaws[F]
}

AlignTests

trait AlignTests[F[_]] extends FunctorTests[F] {
  def laws: AlignLaws[F]
  
  def align[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit
    ArbFA: Arbitrary[F[A]],
    ArbFB: Arbitrary[F[B]],
    ArbFC: Arbitrary[F[C]],
    CogenA: Cogen[A],
    CogenB: Cogen[B],
    CogenC: Cogen[C],
    EqFA: Eq[F[A]],
    EqFB: Eq[F[B]],
    EqFC: Eq[F[C]],
    EqFIorAB: Eq[F[Ior[A, B]]],
    EqFIorIorABC: Eq[F[Ior[Ior[A, B], C]]],
    EqFIorAIorBC: Eq[F[Ior[A, Ior[B, C]]]]
  ): RuleSet
}

object AlignTests {
  def apply[F[_]: Align]: AlignTests[F]
}

Usage Examples

Testing List as Align

import cats.laws.discipline.AlignTests
import cats.syntax.all._

class ListAlignTest extends AnyFunSuite with Checkers {
  checkAll("List.AlignLaws", AlignTests[List].align[Int, String, Double])
}

Testing IO and Par[IO] as Parallel

import cats.laws.discipline.ParallelTests
import cats.effect.IO
import cats.effect.Par

class IOParallelTest extends AnyFunSuite with Checkers {
  checkAll("IO.ParallelLaws", ParallelTests[IO, Par[IO, *]].parallel[Int, String, Double])
}

Custom Align Law Verification

import cats.laws.AlignLaws
import cats.syntax.all._
import cats.data.Ior

val laws = AlignLaws[List]

// Verify align associativity
val assocLaw = laws.alignAssociativity(
  List(1, 2),
  List("a", "b", "c"),
  List(true, false)
)
assert(assocLaw.lhs == assocLaw.rhs)

// Verify alignWith consistency
val alignWithLaw = laws.alignWithConsistent(
  List(1, 2),
  List("a", "b"),
  (ior: Ior[Int, String]) => ior.fold(_.toString, identity, (i, s) => s"$i-$s")
)
assert(alignWithLaw.lhs == alignWithLaw.rhs)