Laws and Where HKJ Bends Them

The instances that bend a law, what equal means in a law, and which laws hkj-test checks.

Where an instance bends a law

InstanceLaw it bends, and howWhat to do
Validatedap agrees with flatMap, which MonadLaws does not check: ap, and map2 to map5, keep every error, while flatMap stops at the firstCombine independent checks with map2 to map5 or traverse, and chain a check that needs the one before with flatMap
TryLeft identity: flatMap turns a step that throws into a Failure, where calling the step throwsNothing: catching is what Try is for, and a step that returns keeps the law
CompletableFutureHandling a raised error is the handler: the handler receives a raised CompletionException's cause, not the exceptionRaise the cause itself
doubleAddition, doubleMultiplicationAssociativity: each + or * rounds, so (0.1 + 0.2) + 0.3 is 0.6000000000000001, and 0.1 + (0.2 + 0.3) is 0.6Total money with bigDecimalAddition, and compare double results within a tolerance
doubleAdditionLeft and right identity: adding 0.0 turns -0.0 into 0.0, which Double.equals treats as unequalCompare with ==, or within a tolerance
bigDecimalAdditionLeft and right identity, by equals: added to ZERO, new BigDecimal("1E+3") comes back as 1000, equal to it by compareTo onlyCompare with compareTo, or keep a price at its usual scale of two
Maybe, Optional, List, StreamLeft identity, for a null value: of(null) gives an empty value, so a step that turns null into a value fails itKeep null out of of, and hold a value that may be absent in a Maybe

An instance built from a Semigroup or Monoid you pass, such as a Writer's, keeps its laws only when what you pass keeps its own. A Writer that logs with doubleAddition fails associativity and right identity. An instance not listed keeps its laws as far as the library's tests check them, which Which laws hkj-test checks shows.

What equal means in a law

A law says that two expressions are equal. For a value, such as an Either or a List, that is equals once both are narrowed. A deferred type, such as IO or VTask, is equal when running both gives equal results, and a Free program when interpreting both does. A function-shaped type, such as State or Reader, is equal when running both from the same input gives equal results.

A Stream is equal to another when collecting both gives equal lists. A stream can be read only once, so a checker that uses one value on both sides of a law, as most of hkj-test's do, throws IllegalStateException on a Stream. Check a Stream instance's laws by hand, building each side from a fresh stream. Comparing two deferred values runs each of them, so an effect that appears on both sides of a law runs once for each side. For a type with one parameter, KindEquivalence.byEqualsAfter, in hkj-test, builds each of these equalities from a function that narrows, runs or interprets.

A function, such as a Natural, is equal to another when both give equal results for every input. A test can try only a few inputs, as the naturality law does.

Which laws hkj-test checks

Each type class links the section that states its laws.

Laws ofhkj-test checks
SemigroupNot supported yet
MonoidNot supported yet
FunctorFunctorLaws: identity, composition
ApplicativeApplicativeLaws: identity, homomorphism, interchange, composition
SelectiveSelectiveLaws: left pure, right pure
MonadMonadLaws: left identity, right identity, associativity
MonadErrorNot supported yet
AlternativeNot supported yet
MonadZeroNot supported yet
FoldableNot supported yet
TraverseTraverseLaws: identity
NaturalNot supported yet
BifunctorBifunctorLaws: identity, composition, first consistency, second consistency
ProfunctorNot supported yet
MonadReaderNot supported yet
MonadStateNot supported yet
MonadWriterNot supported yet
A type's widen and narrowKindHelperLaws: round trip, idempotency, edge cases

Each checker for a type class over one parameter takes the instance, a few values, and an equality from KindEquivalence.byEqualsAfter. BifunctorLaws takes an equality over Kind2 values, and KindHelperLaws takes the type's widen and narrow. Prove your instance runs them on a type of your own.

Where a type class reads "Not supported yet", check its laws in a test of your own, as the sections for MonadError and Alternative do. TraverseLaws checks identity only, so a Traverse instance's naturality and composition need a test of your own too.

SelectiveLaws checks left pure and right pure, two properties that the laws of the Selective paper do not require, and it checks none of the paper's laws. The library's instances keep both properties. The paper does not require a select to skip the function for a Right, so an instance of your own can keep its laws and still fail right pure.

The claims on this page are tested in LawsBookTest.java and in the tests of the pages each row links.


Previous: Foundations FAQ Next: Example Index