“I/O monad”
input/output is a side effect.
pure functions are not supposed to have side effects
IO is a monad to sequence side effects. It also allows us to push effects to the “edges” of our program.
putStrLn :: String -> IO ()
getLine :: IO String
main2 :: IO ()
main2 = do
putStrLn "Hello, world!"
putStr "Enter your name: "
name <- getLine
putStrLn ("Hello, " ++ name)
Test-driven development.
Lots of languages have “unit test” frameworks/libraries. (jUnit, unittest(python), pytest, cppunit, …)
Unit tests pertain to one small piece of code – one function or one class. A lot of unit tests come down to comparing actual results with expected results.
Straightforward testing library for Haskell is HUnit.
Exercise: we want a function that can determine whether a string is a “palindrome”. Example: “racecar”
Write one or more tests first.
test_racecar = isPalindrome "racecar" @?= True
test_car = isPalindrome "car" @?= False
test_abba = isPalindrome "abba" @?= True
test_abcba = isPalindrome "abcba" @?= True
test_abcdba = isPalindrome "abcdba" @?= False
test_wspaces = isPalindrome "mr owl ate my metal worm" @?= True
test_wpunct = isPalindrome "ab.cd,c!ba" @?= True
test_alphacase = isPalindrome "abBA" @?= True
test_numbers = isPalindrome "a2bb3a" @?= False
actual @?= expected
expected @=? actual
main =
runTestTT $ TestList
[ TestLabel "racecar" (TestCase test_racecar)
, TestLabel "car" (TestCase test_car)
, TestLabel "abba" (TestCase test_abba)
, TestLabel "abcba" (TestCase test_abcba)
, TestLabel "abcdba" (TestCase test_abcdba)
, TestLabel "wspaces" (TestCase test_wspaces)
, TestLabel "wpunct" (TestCase test_wpunct)
, TestLabel "alphacase" (TestCase test_alphacase)
, TestLabel "numbers" (TestCase test_numbers)
]
Idea: instead of manually coming up with specific examples to use in tests, we state a general property (using code) and the test system comes up with LOTS of randomly-generated examples and checks whether the property holds (is true).
Original property-based testing tool was QuickCheck. It was developed for Haskell and Erlang by John Hughes. The idea was so compelling, many other languages started adopting it.
Let’s try an example with sorting.
prop_sort_preserves_length :: [Int] -> Property
prop_sort_preserves_length xs =
length xs === length (sort xs)
prop_in_order :: [Int] -> Bool
prop_in_order xs =
all (\(x,y) -> x <= y) $ zip ys (tail ys)
where ys = sort xs
prop_contains_same_elts :: [Int] -> Bool
prop_contains_same_elts xs =
all (\x -> x `elem` ys) xs
where ys = sort xs
split :: [a] -> ([a], [a])
split [] = ([], [])
split [x] = ([x], [])
split (x1:x2:xs) = (x1:ys, x2:zs)
where (ys,zs) = split xs
merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys)
| x <= y = x : merge xs (y:ys)
| otherwise = y : merge (x:xs) ys
sort :: Ord a => [a] -> [a]
sort [] = []
sort [x] = [x]
sort xs = merge (sort ys) (sort zs)
where (ys, zs) = split xs