Haskell/Hierarchical libraries/Maybe
The Maybe data type is a means of being explicit that you are not sure that a function will be successful when it is executed.
[edit] Motivation
Many languages require you to guess what they will do when a calculation did not finish properly. For example, an array lookup signature may look like this in pseudocode:
getPosition(Array a, Value v) returns Integer
But what happens if it doesn't find the item? It could return a null
value, or the integer '-1' which would also be an obvious sign that something went wrong. But there's no way of knowing what will happen without examining the code for this procedure to see what the programmer chose to do. In a library without available code this might not even be possible.
The alternative is to explicitly state what the function should return (in this case, Integer), but also that it might not work as intended — Maybe Integer. This is the intention of the Maybe datatype. So in Haskell, we could write the above signature as:
getPosition :: Array -> Value -> Maybe Integer
If the function is successful you want to return the result; otherwise, you want to return an explicit failure. This could be simulated as a tuple of type (Bool, a)
where a is the "actual" return type of the function. But what would you put in the a slot if it failed? There's no obvious answer. Besides which, the Maybe type is easier to use and has a selection of library functions for dealing with values which may fail to return an explicit answer.
[edit] Definition
The Standard Prelude defines the Maybe type as follows, and more utility functions exist in the Data.Maybe library.
data Maybe a = Nothing | Just a
The type a is polymorphic and can contain complex types or even other monads (such as IO () types).
[edit] Library functions
The module Data.Maybe
, in the standard hierarchical libraries, contains a wealth of functions for working with Maybe values.
[edit] Querying
There are two obvious functions to give you information about a Maybe value.
[edit] isJust
This returns True if the argument is in the form Just _
.
isJust :: Maybe a -> Bool isJust (Just _) = True isJust Nothing = False
[edit] isNothing
The dual of isJust
: returns True if its argument is Nothing
.
isNothing :: Maybe a -> Bool isNothing (Just _) = False isNothing Nothing = True
[edit] Getting out
There are a handful of functions for converting Maybe values to non-Maybe values.
[edit] maybe
maybe
is a function that takes a default value to use if its argument is Nothing
, a function to apply if its argument is in the form Just _
, and a Maybe value.
maybe :: b -> (a -> b) -> Maybe a -> b maybe _ f (Just x) = f x maybe z _ Nothing = z
[edit] fromMaybe
A frequent pattern is to use the maybe
function, but not want to change the value if it was a Just
. That is, call maybe
with the second parameter being id
. This is precisely fromMaybe
.
fromMaybe :: a -> Maybe a -> a fromMaybe z = maybe z id
[edit] fromJust
There are certain occasions when you know a function that ends in a Maybe value will produce a Just
. In these cases, you can use the fromJust
function, which just strips off a Just
constructor.
fromJust :: Maybe a -> a fromJust (Just x) = x fromJust Nothing = error "fromJust: Nothing"
[edit] Lists and Maybe
Lists are, in some ways, similar to the Maybe datatype (indeed, this relationship will be further explored when you learn about monads). As such, there are a couple of functions for converting between one and the other.
[edit] listToMaybe
This function, and the following one, makes a lot of sense when you think about Maybe and list values in terms of computations (which will be more fully explained in the section on Advanced monads).
With lists, []
represents a failed computation. With Maybe, Nothing
does. listToMaybe
converts between the list and Maybe monad. When the parameter (in the list monad) indicated a successful computation, only the first solution is taken to place in the Maybe value.
listToMaybe :: [a] -> Maybe a listToMaybe [] = Nothing listToMaybe (x:_) = Just x
[edit] maybeToList
The obvious opposite of listToMaybe
.
maybeToList :: Maybe a -> [a] maybeToList Nothing = [] maybeToList (Just x) = [x]
[edit] Lists manipulation
Finally, there are a couple of functions which are analogues of the normal Prelude list manipulation functions, but specialised to Maybe values.
[edit] Continue on some failures (like 'or')
[edit] catMaybes
Given a list of Maybe values, catMaybes
extracts all the values in the form Just _
, and strips off the Just
constructors. This is easily defined with a list comprehension, as we showed in the pattern matching chapter:
catMaybes :: [Maybe a] -> [a] catMaybes ms = [ x | Just x <- ms ]
[edit] mapMaybe
mapMaybe
applies a function to a list, and collects the successes. It can be understood as a composition of functions you already know:
mapMaybe :: (a -> Maybe b) -> [a] -> [b] mapMaybe f xs = catMaybes (map f xs)
But the actual definition may be more efficient and traverse the list once:
mapMaybe :: (a -> Maybe b) -> [a] -> [b] mapMaybe _ [] = [] mapMaybe f (x:xs) = case f x of Just y -> y : mapMaybe f xs Nothing -> mapMaybe f xs
[edit] Stop on failure
[edit] sequence
Sometimes you want to collect the values if and only if all succeeded:
sequence :: [Maybe a] -> Maybe [a] sequence [] = Just [] sequence (Nothing:xs) = Nothing sequence (Just x:xs) = case sequence xs of Just xs' -> Just (x:xs') _ -> Nothing