A Java interface describes available operations, but it cannot express the mathematical properties those operations should obey. Therefore a class or lambda may compile while still being an invalid typeclass instance.
Milestone 0.3 turns the documented algebraic contracts into executable test rules.
For an Eq<A> instance:
a ~ a
a ~ b <=> b ~ a
a ~ b && b ~ c => a ~ c
Ord<A> refines Eq<A> through compare(a, b) == 0.
The test suite verifies:
- self comparison yields zero;
- reversing arguments reverses the sign of comparison;
- the less-than-or-equal relation is transitive;
eqv(a, b)agrees with zero comparison.
An Ord instance may intentionally consider different domain values equivalent. For example, ordering Person only by age means two people of equal age compare as zero.
A Semigroup<A> must be associative:
(a <> b) <> c == a <> (b <> c)
The two results are compared using an explicit Eq<A> instance rather than Objects.equals. This preserves the project's typeclass semantics.
A Monoid<A> inherits semigroup associativity and adds an identity element empty():
empty <> a == a
a <> empty == a
Example-based unit tests can demonstrate known cases but cannot provide meaningful confidence about universal algebraic rules. Property-based testing generates many inputs and shrinks failing cases to smaller counterexamples.
The project uses jqwik through the JUnit Platform.
Reusable helpers are located in test scope:
src/test/java/io/codeswarm/typeclasses/laws/
They return booleans and do not depend on an assertion library. This keeps them reusable by both property tests and focused example-based tests.
Integer addition is associative and has identity 0, so it is a lawful monoid.
Integer subtraction is not associative:
(10 - 3) - 2 = 5
10 - (3 - 2) = 9
UnlawfulInstancesTest intentionally demonstrates this failure. The subtraction operation can implement Semigroup<Integer> syntactically, but it does not satisfy the semigroup law semantically.
Milestone 0.3 uses laws as internal verification infrastructure. Publishing them as production API would create a compatibility commitment before the project has enough experience with their shape.
A future milestone may extract them into a dedicated module if users need reusable law suites.
The law helpers introduced in 0.3 are reused to verify composed equality instances for Option, Either and Validated.
The project checks the same Eq laws:
- reflexivity,
- symmetry,
- transitivity.
This demonstrates an important principle: composing an instance should preserve the algebraic contract when the component instances are lawful.
The concatenation instance for NonEmptyList<A> reuses the existing SemigroupLaws.associative helper. Derived Eq<NonEmptyList<A>> also reuses the existing EqLaws harness.
This demonstrates the intended layering:
ADT -> typeclass instance -> reusable law -> property-based verification
No special-purpose law framework is added for NonEmptyList; existing laws remain the single source of truth.