Skip to content
UoL CS Notes

Lecture 21-2

COMP105 Lectures

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

  1.  safeTail :: [a] -> Maybe [a]
     safeTail []     = Nothing
     safeTail x:xs   = Just xs
    
  2.  safeDiv :: Int -> Int -> Maybe Int
     safeDiv _ 0 = Nothing
     safeDiv x y = Just (div x y)
    
  3.  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)