Thursday, January 2, 2014

HASKELL QUICKCHECK GENERATOR COMBINATORS

HASKELL QUICKCHECK GENERATOR COMBINATORS

REVISED: Thursday, Februray 8, 2024




Haskell QuickCheck Generator Combinators.

I. GENERATOR COMBINATORS

QuickCheck is a tool for testing Haskell programs automatically. The programmer provides a specification of the program, in the form of properties which functions should satisfy, and QuickCheck then tests that the properties hold in a large number of randomly generated cases. Specifications are expressed in Haskell, using combinators defined in the QuickCheck library. QuickCheck provides combinators to define properties, observe the distribution of test data, and define test data generators.

Haskell QuickCheck includes a set of combinators that allow us to create custom instances for our own types.

A. choose

The first of these combinators is `choose`.

choose takes an interval and returns a random element from that interval. For example, the following is a randomly chosen set of numbers between `0` and `9`.

GHCi, version 9.8.1: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude>

Prelude> import Test.QuickCheck
Prelude>

Prelude> import Control.Monad
Prelude>

Prelude> import Data.List
Prelude>

Prelude> :type choose
choose :: System.Random.Random a => (a, a) -> Gen a
Prelude>

Prelude> sample $ choose (0, 9)
Loading package bytestring-0.9.2.1 ... linking ... done.
Loading package Win32-2.2.2.0 ... linking ... done.
Loading package array-0.4.0.0 ... linking ... done.
Loading package deepseq-1.3.0.0 ... linking ... done.
Loading package old-locale-1.0.0.4 ... linking ... done.
Loading package time-1.4 ... linking ... done.
Loading package random-1.0.1.1 ... linking ... done.
Loading package containers-0.4.2.1 ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
Loading package QuickCheck-2.5.1.1 ... linking ... done.
2
5
2
9
5
3
6
4
5
3
5
it :: ()
Prelude>

B. elements

A second combinator is `elements` which returns a generator that produces values drawn from an input list.

elements takes a list and returns a random element from that list. For example, the following is a list of numbers between `10` and `90`.

Prelude> :type elements
elements :: [a] -> Gen a
Prelude>

Prelude> sample $ elements [10, 20..90]
90
90
30
60
80
70
70
60
20
30
40
it :: ()
Prelude>

C. oneof

A third combinator is `oneof` which allows us to randomly choose between multiple generators.

Prelude> :type oneof
oneof :: [Gen a] -> Gen a
Prelude>

Prelude> sample $ oneof [elements [10,20..90], choose (0, 9)]
3
6
8
8
2
3
4
2
8
20
8
it :: ()
Prelude>

D. frequency

A fourth combinator is 'frequency' which allows us to build weighted combinations of individual generators.

Prelude> :type frequency
frequency :: [(Int, Gen a)] -> Gen a
Prelude>

E. forAll

A fifth combinator is 'forAll' which can be used to check the output of a custom generator.

Prelude> :type forAll
forAll
    :: (Testable prop, Show a) => Gen a -> (a -> prop) -> Property
Prelude>

II. CHANGING NUMBER OF QUICKCHECK TESTS

Prelude> let quickCheckN n = quickCheckWith $ stdArgs { maxSuccess = n }
Loading package bytestring-0.9.2.1 ... linking ... done.
Loading package Win32-2.2.2.0 ... linking ... done.
Loading package array-0.4.0.0 ... linking ... done.
Loading package deepseq-1.3.0.0 ... linking ... done.
Loading package old-locale-1.0.0.4 ... linking ... done.
Loading package time-1.4 ... linking ... done.
Loading package random-1.0.1.1 ... linking ... done.
Loading package containers-0.4.2.1 ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
Loading package QuickCheck-2.5.1.1 ... linking ... done.
quickCheckN :: Testable prop => Int -> prop -> IO ()
Prelude>

Prelude> let isOrdered (x1:x2:xs) = x1 <= x2 && isOrdered (x2:xs)
isOrdered :: Ord a => [a] -> Bool
Prelude>

Prelude> let isOrdered _ = True
isOrdered :: t -> Bool
Prelude>

Prelude> let prop_sort_isOrdered = isOrdered . sort
prop_sort_isOrdered :: Ord a => [a] -> Bool
Prelude>

Prelude> quickCheckN 10000 prop_sort_isOrdered
+++ OK, passed 10000 tests.
it :: ()
Prelude>

III. CONCLUSION

In this tutorial, you have been introduced to Haskell QuickCheck generator combinators.

IV. REFERENCES

Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.

Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.

Goerzen, J. & O'Sullivan, B. &  Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.

Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.

Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.

Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.