liveBook · Manning

Algebraic law

As explained in Learn Haskell by Example

Property is the QuickCheck type representing a testable property, including properties augmented with conditions or classifications.

An algebraic law is a characteristic that an implementation should satisfy for every relevant input. In QuickCheck, such a law is written as a property: often a predicate returning Bool, but also a value of QuickCheck’s Property type or a function returning either. Rather than list expected outputs, run the property against generated inputs to look for counterexamples. section 9.1.1 section 9.3.1

Start with a relationship that should remain true regardless of particular data. A sorting function should produce a sorted list; applying rot13 twice should return the original string; an alphabet rotation should return a character contained in its alphabet. Equality checks against fixed expected values are a simple form of the same idea, but laws can combine predicates and rules into broader checks. section 9.1.1 section 9.3.1

Listing 9.1 Predicates on the sortedness of lists
sorted :: Ord a => [a] -> Bool
sorted [] = True      #1
sorted [_] = True     #1
sorted (x : y : xs) = x <= y && sorted (y : xs)  #2

sorts :: Ord a => ([a] -> [a]) -> [a] -> Bool
sorts f input = sorted $ f input  #3

This pair of predicates separates the invariant, sorted, from the property that applies it to any list-transforming function. The Ord constraint supplies the comparison needed to check adjacent elements. section 9.1.1

Check a symmetry law

A symmetry property can be written as prop_rot13Symm s = s == rot13 (rot13 s). Running quickCheck prop_rot13Symm tests generated strings. An incorrect refactoring fails with the counterexample "a"; QuickCheck shrinks failing inputs toward a smaller counterexample when it can. section 9.3.1

Refine a property when generated tests are misleading or insufficient:

  • Give the property an explicit type, such as [Int] -> Bool, to avoid type defaulting to unit values that make sortedness trivial. section 9.5.1
  • Use verbose to inspect generated cases, and collect or label to report how input categories are distributed. section 9.5.1
  • Use a precondition with ==> when only inputs meeting a rule matter, while watching discarded tests. section 9.5.1
  • Use cover to require and report a desired proportion of a category, such as non-trivial lists. section 9.5.1

Properties can become a runnable test suite. Name them with the prop_ prefix, use Template Haskell’s quickCheckAll to find and run them in a module, combine suite results in Main, and return a success or failure exit code so stack test reports the result. Modifiers can adjust a property’s run, for example withMaxSuccess, noShrinking, or cover. section 9.5.3 section 9.6

How Learn Haskell by Example covers it

How deep it goes
Defines Bool and Property forms, demonstrates modifiers and shrinking, and defers custom Testable instances.
Examples
Uses sorted lists, rot13 symmetry, alphabet closure, and arithmetic properties in test suites.
What it assumes
Starts with Bool-returning predicates and introduces QuickCheck Property without requiring custom Testable instances.
What you can do after
Apply it via QuickCheck to run generated invariant checks and organize prop_-named tests into Stack suites.
How it connects
Connects QuickCheck properties to random generators, shrinking, preconditions, coverage, Template Haskell, and Stack.

Also explained in

Compare how 2 books explain algebraic law →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Haskell by Example. The section links point to the parts of the book it draws on.