Pattern Matching
Prerequisites: Tuples
Pattern matching lets you define a function by the shape of its input. Equations are tried from top to bottom, and the first one whose pattern matches is used:
describeNumber :: Int -> String
describeNumber 0 = "zero"
describeNumber 1 = "one"
describeNumber _ = "many"
isVowel :: Char -> Bool
isVowel c = c `elem` "aeiou"
main :: IO ()
main = do
mapM_ (putStrLn . describeNumber) [0, 1, 7]
print (filter isVowel "functional")
zero
one
many
"uioa"
_ is a wildcard: it matches anything and ignores the value.
Matching lists
A list is either [] or x : xs, so those are the two patterns you
match on. Parentheses are required around x : xs:
describeList :: [Int] -> String
describeList [] = "empty"
describeList [x] = "one element: " ++ show x
describeList [x, y] = "two elements: " ++ show x ++ " and " ++ show y
describeList (x : _) = "starts with " ++ show x
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x : _) = Just x
main :: IO ()
main = do
mapM_ (putStrLn . describeList) [[], [5], [5, 6], [5, 6, 7]]
print (safeHead "abc")
print (safeHead ([] :: [Int]))
empty
one element: 5
two elements: 5 and 6
starts with 5
Just 'a'
Nothing
safeHead never crashes: it returns Nothing for an empty list and
Just x otherwise. You'll meet Maybe properly in Level 2.
Matching tuples
addPairs :: (Int, Int) -> (Int, Int) -> (Int, Int)
addPairs (a, b) (c, d) = (a + c, b + d)
third :: (a, b, c) -> c
third (_, _, z) = z
main :: IO ()
main = do
print (addPairs (1, 2) (10, 20))
print (third ('a', True, "yes"))
(11,22)
"yes"
case expressions
case does pattern matching anywhere inside an expression:
weekday :: Int -> String
weekday n = case n of
1 -> "Monday"
2 -> "Tuesday"
6 -> "Saturday"
7 -> "Sunday"
_ -> "another day"
main :: IO ()
main = do
putStrLn (weekday 2)
putStrLn (weekday 5)
putStrLn (case words "hello pattern matching" of
(first : rest) -> first ++ " + " ++ show (length rest) ++ " more"
[] -> "no words")
Tuesday
another day
hello + 2 more
As-patterns
name@pattern gives a name to the whole value while also matching its
parts:
firstLetter :: String -> String
firstLetter "" = "empty string"
firstLetter whole@(c : _) = "The first letter of " ++ whole ++ " is " ++ [c]
main :: IO ()
main = putStrLn (firstLetter "Haskell")
The first letter of Haskell is H
Incomplete patterns
If no equation matches, the program crashes. Compile with -Wall (projects
created by cabal init turn it on for you; for a single file, run
ghc -Wall file.hs) and GHC warns about every case you
forgot. Handling every case is one of the habits that makes Haskell
programs so reliable.
Practice
Write secondElement :: [a] -> Maybe a that returns the second element
of a list, or Nothing if there isn't one.
Show solution
secondElement :: [a] -> Maybe a
secondElement (_ : y : _) = Just y
secondElement _ = Nothing
main :: IO ()
main = do
print (secondElement [1, 2, 3 :: Int])
print (secondElement "a")
Just 2
Nothing