Laws for parallel operations and alignment of functors, enabling concurrent execution and data alignment.
import cats.laws._
import cats.laws.discipline._
import cats.Parallel
import cats.Align
import cats.data.Iortrait 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]
}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]
}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]
}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]
}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]
}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]
}import cats.laws.discipline.AlignTests
import cats.syntax.all._
class ListAlignTest extends AnyFunSuite with Checkers {
checkAll("List.AlignLaws", AlignTests[List].align[Int, String, Double])
}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])
}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)