Lecture 21-2
Maybe
data Maybe a = Just a | Nothing
> :t Just "hello"
Maybe [Char]
> :t Just False
Maybe Bool
> :t Nothing
Maybe a
From here you can see that when you call the Just
constructor you force the type variable a
to be a particular type and if you call Nothing
then no type is assigned to a
.
The Maybe
type is used in pure functional code that might fail.
safe_head [] = Nothing
safe_head (x:_) = Just x
> safe_head [1,2,3]
Just 1
> safe_head []
Nothing
Given that the function will always give an output this means that it wont give an exception and crash the program.
Example
safe_get_heads list =
let
mapped = map safe_head list
filtered = filter (/=Nothing) mapped
unjust = (\ x -> case x of Just a -> a)
in
map unjust filtered
> safe_get_heads [[],[1],[2,3]]
[1,2]
This code will not crash in the event that an empty list is given to head.
Exceptions in Haskell
Haskell does include support for exceptions
> head []
*** Exception: Prelude.head: empty list
Exceptions are not pure functional:
- Every function returns exactly one value.
- You can’t catch exceptions in pure functional code.
- Exceptions are mostly used in IO code.
The Maybe
type provides a way to do exception-like behaviour in pure functional code.
Can this function fail for some inputs?
- Use the
Maybe
type.
Exceptions should only be used in IO code:
- File not found, could not connect to server, etc.
- These are unpredictable events.
Either
data Either' a b = Left a | Right b
> :t Left 'a'
Either Char 'b'
> :t Right 'b'
Either a Char
The Either
type is useful if you want to store different types in the same list:
is_left (Left _) = True
is_left _ = False
> let list = [Left "one", Right 2, Left "three", Right 4]
> map is_left list
[True,False,True,False]
Example - Filtering off Left
get_lefts list =
let
filtered = filter is_left list
unleft = (\ (Left x) -> x)
in
map unleft filtered
> get_lefts list
["one","three"]
Example - Squaring Mixed Number Types
square (Left x) = Left (x ** 2)
square (Rightx) = Right (x ^ 2)
> let nums = [Left pi, Right (4::Int), Left 2.7182]
> map square nums
[Left 9.86,Right 16,Left 7.38]
Meaningful Error Messages
Either
can also be used to give detailed error messages.
safe_head_either [] = Right "empty list"
safe_head_either (x:xs) = Left x
> safe_head_either []
Right "empty list"
> safe_head_either [1,2,3]
Left 1
This is required as a function can’t return values of two different types.
Exercises
-
safeTail :: [a] -> Maybe [a] safeTail [] = Nothing safeTail x:xs = Just xs
-
safeDiv :: Int -> Int -> Maybe Int safeDiv _ 0 = Nothing safeDiv x y = Just (div x y)
-
safeGet :: [a] -> Int Either a String safeGet x y | x < 0 = Right "Negative index." | length x <= y = Right "Index too large." | otherwise = Left (x !! y)