The Maybe type
In addition to the list type, Haskell has other parameterized types too. Let’s look at a very common and useful one: the Maybe
type.
Sometimes an operation doesn’t have a valid return value (E.g. division by zero.). We have a couple of options in this situation. We can use an error value, like -1
. This is a bit ugly, not always possible. We can throw an exception. This is impure. In some other languages we would return a special null value that exists in (almost) all types. However Haskell does not have a null.
The solution Haskell offers us instead is to change our return type to a Maybe
type. This is pure, safe and neat. The type Maybe a
has two constructors: Nothing
and Just
. Nothing
is just a constant, but Just
takes a parameter. More concretely:
Type | Values |
---|---|
Maybe Bool | Nothing , Just False , Just True |
Maybe Int | Nothing , Just 0 , Just 1 , … |
Maybe [Int] | Nothing , Just [] , Just [1,1337] , … |
You can think of Maybe a
as being a bit like [a]
except there can only be 0 or 1 elements, not more. Alternatively, you can think of Maybe a
introducing a null value to the type a
. If you’re familiar with Java, Maybe Integer
is the Haskell equivalent of Java’s Optional<Integer>
.
You can create Maybe
values by either specifying Nothing
or Just someOtherValue
:
Prelude> :t Nothing
Nothing :: Maybe a
Prelude> Just "a camel"
Just "a camel"
Prelude> :t Just "a camel"
Just "a camel" :: Maybe [Char] -- the same as Maybe String
Prelude> Just True
Just True
Prelude> :t Just True
Just True :: Maybe Bool
-- given a password, return (Just username) if login succeeds, Nothing otherwise
login :: String -> Maybe String
login "f4bulous!" = Just "unicorn73"
login "swordfish" = Just "megahacker"
login _ = Nothing
You use a Maybe
value by pattern matching on it. Usually you define patterns for the Nothing
and Just something
cases. Some examples:
-- Multiply an Int with a Maybe Int. Nothing is treated as no multiplication at all.
perhapsMultiply :: Int -> Maybe Int -> Int
perhapsMultiply i Nothing = i
perhapsMultiply i (Just j) = i*j -- Note how j denotes the value inside the Just
Prelude> perhapsMultiply 3 Nothing
3
Prelude> perhapsMultiply 3 (Just 2)
6
intOrZero :: Maybe Int -> Int
intOrZero Nothing = 0
intOrZero (Just i) = i
safeHead :: [a] -> Maybe a
safeHead xs = if null xs then Nothing else Just (head xs)
headOrZero :: [Int] -> Int
headOrZero xs = intOrZero (safeHead xs)
headOrZero [] ==> intOrZero (safeHead []) ==> intOrZero Nothing ==> 0
headOrZero [1] ==> intOrZero (safeHead [1]) ==> intOrZero (Just 1) ==> 1
Sidenote: Constructors
As you can see above, we can pattern match on the constructors of Maybe
: Just
and Nothing
. We’ll get back to what constructors mean later. For now it’s enough to note that constructors are special values that start with a capital letter that you can pattern match on.
Other constructors that we’ve already seen include the constructors of Bool
– True
and False
. We’ll introduce the constructors of the list type on the next lecture.
Constructors can be used just like Haskell values. Constructors that take no arguments like Nothing
, and False
are just constants. Constructors like Just
that take an argument behave like functions. They even have function types!
Prelude> :t Just
Just :: a -> Maybe a
Exercises:
All exercises can be found in Set2a and Set2b. Please pay attention in the title of the exercise in which file the exercises of this section can be found.
Exercises from 2a:
You can check your current points from the blue blob in the bottom-right corner of the page.