A. Testing.hs

import Test.QuickCheck
import Data.Char
import Data.List

ourConfig = defaultConfig {
  -- we'll override the default of 100, for kicks
  configMaxTest = 500
}

-- forall x. x * 1 == x
multiplicativeIdentity x = x * 1 == x

-- forall a b. a + b == b + a
additionCommutes a b = a + b == b + a

ourCheck :: (Testable a) => a -> IO ()
ourCheck = check ourConfig

-- DeMorgan's Law (1)
demorgan1 p q = not (p && q) == ((not p) || (not q))

-- DeMorgan's Law (2)
demorgan2 p q = not (p || q) == ((not p) && (not q))

-- fails (subtraction is not commutative)
subtractionCommutes a b = a - b == b - a

-- fails
testTake :: String -> Bool
testTake s = length (take 5 s) == 5
-- fixed
testTake' :: String -> Bool
testTake' s = length (take 5 s) <= 5

-- division by zero
divisionInverse a b = (a `div` b) * b + (a `mod` b) == a
-- fixed
divisionInverse' a b = b /= 0 ==> (a `div` b) * b + (a `mod` b) == a

-- TDD
f :: [a] -> [a]
f = undefined

-- this is a tad perverse
property1 :: Bool
property1 = f "" == ""

property2 :: Int -> Bool
property2 x = f [x] == [x]

property3 :: [Int] -> [Int] -> Bool
property3 x y = f (x ++ y) == f y ++ f x

-- once-inhabited
g :: (a -> b) -> (b -> c) -> (a -> c)
g = undefined

instance Show (a -> b) where
  show _ = "<<function>>"

-- mocking (generating functions)
testMap :: (Bool -> String) -> (Int -> Bool) -> [Int] -> Bool
testMap f g x = map (f . g) x == map f (map g x)

-- create your own Arbitrary

data Person = P {
  age :: Int,
  firstName :: String,
  surname :: String,
  gender :: Char
} deriving Show

sumAges :: [Person] -> Int
sumAges = sum . map age

-- for any list of Person (ps) subtracting their ages from (sumAges ps) equals zero
testSumAges ps = foldl' (-) (sumAges ps) (age `map` ps) == 0

instance Arbitrary Char where
    arbitrary = choose ('\32', '\128')
    coarbitrary c = variant (ord c `rem` 4)

-- (>>=) :: Gen a -> (a -> Gen b) -> Gen b
-- return :: a -> Gen a

arbGender :: Gen Char
arbGender = arbitrary >>= \b -> return (if b then 'f' else 'm')

instance Arbitrary Person where
  arbitrary = arbitrary >>= \a ->
              arbitrary >>= \f ->
              arbitrary >>= \s ->
              arbGender >>= \g ->
              return (P a f s g)

{-
  arbitrary = do a <- arbitrary
                 f <- arbitrary
                 s <- arbitrary
                 g <- arbGender
                 return (P a f s g)
-}

  -- todo We won't have time to discuss this function.
  -- It is used for generating functions (where Person appears in the argument list).
  coarbitrary = undefined