Craft3e-0.1.0.2: Chapter9.hs
---------------------------------------------------------------------
--
-- Haskell: The Craft of Functional Programming, 3e
-- Simon Thompson
-- (c) Addison-Wesley, 1996-2011.
--
-- Chapter 9
--
---------------------------------------------------------------------
-- Reasoning about programs
-- ^^^^^^^^^^^^^^^^^^^^^^^^
module Chapter9 where
import Prelude hiding (sum,length,(++),reverse,unzip)
import Test.QuickCheck
-- Testing and verification
-- ^^^^^^^^^^^^^^^^^^^^^^^^
-- A function supposed to give the maximum of three (integer) values.
mysteryMax :: Integer -> Integer -> Integer -> Integer
mysteryMax x y z
| x > y && x > z = x
| y > x && y > z = y
| otherwise = z
prop_mystery :: Integer -> Integer -> Integer -> Bool
prop_mystery x y z =
mysteryMax x y z == (x `max` y) `max` z
-- Definedness and termination
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- A factorial function, giving an undefined result on negative integers.
fact :: Integer -> Integer
fact n
| n==0 = 1
| otherwise = n * fact (n-1)
-- An infinite list
posInts :: [Integer]
posInts = [1, 2 .. ]
-- Induction
-- ^^^^^^^^^
-- The sum function, defined recursively.
sum :: [Integer] -> Integer
sum [] = 0 -- (sum.1)
sum (x:xs) = x + sum xs -- (sum.2)
-- Double every element of an integer list.
doubleAll :: [Integer] -> [Integer]
doubleAll [] = [] -- (doubleAll.1)
doubleAll (z:zs) = 2*z : doubleAll zs -- (doubleAll.2)
-- The property linking the two:
-- sum (doubleAll xs) = 2 * sum xs -- (sum+dblAll)
prop_SumDoubleAll :: [Integer] -> Bool
prop_SumDoubleAll xs =
sum (doubleAll xs) == 2 * sum xs
-- Other functions used in the examples
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- The definitions given here use explicit recursion, rather than applying
-- higher-order functions as may happen in the Prelude definitions.
length :: [a] -> Int
length [] = 0 -- (length.1)
length (z:zs) = 1 + length zs -- (length.2)
(++) :: [a] -> [a] -> [a]
[] ++ zs = zs -- (++.1)
(w:ws) ++ zs = w:(ws++zs) -- (++.2)
-- QuickCheck property
prop_lengthPlusPlus :: [a] -> [a] -> Bool
prop_lengthPlusPlus xs ys =
length (xs ++ ys) == length xs + length ys
reverse :: [a] -> [a]
reverse [] = [] -- (reverse.1)
reverse (z:zs) = reverse zs ++ [z] -- (reverse.2)
-- QuickCheck properties
-- Why does prop_reversePlusPlus' not fail? Because a defaults to ().
-- See note on "QuickCheck and properties over [a]" at the end of
-- Section 9.6.
prop_reversePlusPlus' :: Eq a => [a] -> [a] -> Bool
prop_reversePlusPlus' xs ys =
reverse (xs ++ ys) == reverse xs ++ reverse ys
-- The "right" property here.
prop_reversePlusPlusOops :: [Integer] -> [Integer] -> Bool
prop_reversePlusPlusOops xs ys =
reverse (xs ++ ys) == reverse xs ++ reverse ys
-- The "right" property here.
prop_reversePlusPlus :: [Integer] -> [Integer] -> Bool
prop_reversePlusPlus xs ys =
reverse (xs ++ ys) == reverse ys ++ reverse xs
-- Associativity of ++
prop_assocPlusPlus :: [Integer] -> [Integer] -> [Integer] -> Bool
prop_assocPlusPlus xs ys zs =
(xs ++ ys) ++ zs == xs ++ (ys ++ zs)
unzip :: [(a,b)] -> ([a],[b])
unzip [] = ([],[])
unzip ((x,y):ps)
= (x:xs,y:ys)
where
(xs,ys) = unzip ps
-- Generalizing the proof goal
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- The shunting function
shunt :: [a] -> [a] -> [a]
shunt [] ys = ys -- (shunt.1)
shunt (x:xs) ys = shunt xs (x:ys) -- (shunt.2)
-- QuickCheck property of shunt.
prop_shunt :: [Integer] -> [Integer] -> Bool
prop_shunt xs zs =
shunt (shunt xs zs) [] == shunt zs xs
-- Alternative reverse.
rev :: [a] -> [a]
rev xs = shunt xs [] -- (rev.1)
-- Do they always match?
prop_reverses :: [Integer] -> Bool
prop_reverses xs =
reverse xs == rev xs
-- An alternative definition of the factorial function.
fac2 :: Integer -> Integer
fac2 n = facAux n 1
facAux :: Integer -> Integer -> Integer
facAux 0 p = p
facAux n p = facAux (n-1) (n*p)
-- QuickChecking the two factorials:
prop_facs' :: Integer -> Bool
prop_facs' x =
fact x == fac2 x
prop_facs :: Integer -> Bool
prop_facs x =
(x<0) || fact x == fac2 x