Haskell/Hierarchical libraries/Maybe

From Wikibooks, open books for an open world
< Haskell‎ | Hierarchical libraries
Jump to: navigation, search

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.

Motivation[edit]

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.

Definition[edit]

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).

Library functions[edit]

The module Data.Maybe, in the standard hierarchical libraries, contains a wealth of functions for working with Maybe values.

Querying[edit]

There are two obvious functions to give you information about a Maybe value.

isJust[edit]

This returns True if the argument is in the form Just _.

isJust :: Maybe a -> Bool
isJust (Just _) = True
isJust Nothing  = False

isNothing[edit]

The dual of isJust: returns True if its argument is Nothing.

isNothing :: Maybe a -> Bool
isNothing (Just _) = False
isNothing Nothing  = True

Getting out[edit]

There are a handful of functions for converting Maybe values to non-Maybe values.

maybe[edit]

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

fromMaybe[edit]

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

fromJust[edit]

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"

Lists and Maybe[edit]

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.

listToMaybe[edit]

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

maybeToList[edit]

The obvious opposite of listToMaybe.

maybeToList :: Maybe a -> [a]
maybeToList Nothing  = []
maybeToList (Just x) = [x]

Lists manipulation[edit]

Finally, there are a couple of functions which are analogues of the normal Prelude list manipulation functions, but specialised to Maybe values.

Continue on some failures (like 'or')[edit]

catMaybes[edit]

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 ]
mapMaybe[edit]

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

Stop on failure[edit]

sequence[edit]

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