Partial functions are functions that are not defined for all possible arguments of its specified type.
The most common example of a partial function is
head. It has the innocent-looking type signature of
head :: [a] -> a
head fails when an empty list is given:
head [1,2,3] -- 1 head  -- Exception!
Because of Haskell’s type erasure,
head doesn’t even know what the supplied type is, so there’s no way for it to return a value of type
a when there are no values in the list (besides the obvious fact that there are no values in the list).
In Brent Yorgey’s CIS 194 Haskell course:
head is a mistake! It should not be in the Prelude. Other partial Prelude functions you should almost never use include tail, init, last, and (!!).
Haskell’s official wiki provides a complete list of partial functions in Prelude. Note that some of these functions are considered partial functions because they do not terminate if given an infinite list.
Given all that, I think using something like
head is okay if composed with other functions that guarantee non-emptiness of the list, or if the function type signature is
NonEmpty, a type class which guarantees a list with at least one element.
For example, consider
lastItem, a function which returns the last item in a list:
lastItem :: [a] -> a lastItem = head . reverse
lastItem [1,2] -- 2 lastItem  -- Exception!
In addition, consider
toDigits, a function which, when given an
Integral value, returns a list of its constituent digits:
toDigits :: Integral a => a -> [a] toDigits x | x < 0 = toDigits $ x * (-1) | x < 10 = [x] | otherwise = toDigits (div x 10) ++ [mod x 10]
toDigits 123 -- [1,2,3] toDigits 0 --  toDigits (-43) -- [4,3]
A function like
toDigits guarantees a non-empty list, and when combined with
lastItem, we can get the last digit of an
lastDigit :: Integral a => a -> Int lastDigit = lastItem . digits
lastDigit 123 -- 3 lastDigit 01 -- 1 lastDigit (-1) -- 1
(!!), which accesses an element in a list by index. If the index provided is too large, an exception is thrown:
(!!) [1,2,3] 0 -- 1 (!!) [1,2,3] 4 -- Exception!
An idea is to wrap
(!!) with the
Maybe data type in a function like this:
findByIndex :: [a] -> Int -> Maybe a findByIndex xs index | index >= length xs = Nothing | otherwise = Just ((!!) xs index)
findByIndex [1,2,3] 0 -- Just 1 findByIndex [1,2,3] 4 -- Nothing
I’m surprised that such commonly used functions in the Prelude are so dangerous, so it’s good to pay attention when using them. Partial functions like
head are easy to replace with pattern matching, but others may be harder to supplant.