Haskell/Understanding monads/Maybe
From Wikibooks, the open-content textbooks collection
Maybe monad
One of the simplest monads is the Maybe monad, and can be used for computations whose result can "go wrong", for instance a square root: the square root of a negative number is not defined (for this example we do not consider complex numbers).
Contents |
[edit] Definition
Maybe has two constructors, Just and Nothing; Just indicates that the computation was successful, and Nothing that something went wrong. So, our implementation of a "safe" square root function could be:
safeSqrt :: (Floating a, Ord a) => a -> Maybe a safeSqrt x | x >= 0 = Just (sqrt x) | otherwise = Nothing
[edit] Safe functions
We could now decide to write similar "safe functions" for all functions with limited domains, such as division, logarithm and inverse trigonometric functions (safeDiv, safeLog, safeArcSin, etc.). However, when we try to combine them we run into a problem: they output a Maybe a, but take an a as an input. As a result, before each application, we have to check whether the previous operation was successful:
safeLogSqrt :: (Floating a, Ord a) => a -> Maybe a safeLogSqrt x = case safeSqrt x of Just root -> safeLog root Nothing -> Nothing
You can see the problem: the code looks ugly already when using one concatenation. If you had multiple concatenations the code would get even worse. This is in stark contrast to the easy concatenation that we get with "unsafe" functions:
unsafeLogSqrt = log . sqrt
Monads allow exactly this: to concatenate operations easily. This works not just for computations whose success is not guaranteed (Maybe), but also for computations whose number of results is not given (lists are monads too!), operations that pass around some kind of state (parsers are a classical example) or have "side effects", such as the IO monad for input/output.
[edit] Maybe as an instance of Monad
The instantiation of Maybe as a monad occurs defining the bind operator:
Just value >>= function = function value Nothing >>= _ = Nothing
In words, if the initial Maybe value is Nothing, the binding operation returns Nothing without even evaluating the function; otherwise, the function is evaluated and its result returned.
The return implementation is trivial: to make any value of type a into a Maybe a, we simply use the Just constructor, therefore:
return = Just
[edit] Using Monadic Code with Maybe
Looking back at our "safe" functions, what we want is the ability to take a Maybe value (a result of a previous computation), extract its associated value (if possible), and apply the new function to it; if the value was Nothing, however, we stop there and return Nothing. This is exactly what the (>>=) operator does:
> Just 1000 >>= safeSqrt >>= safeLog Just 3.4538776394910684 > Just (-1000) >>= safeSqrt >>= safeLog Nothing
Every additional operation is just another >>= safeOperation term—no checks required, since (>>=) takes care of that. You can obtain a combination identical to the (.) operator for unsafe functions using the (<=<) operator from the Control.Monad module:
import Control.Monad((<=<)) safeLogSqrt' = safeLog <=< safeSqrt
The other important operator for monads is return, which creates a monadic value out of a standard one; in the case of Maybe it is simply the Just constructor.
> return 1 :: Maybe Int Just 1
[edit] Openness
A peculiarity of the Maybe monad is that it is "open": if we have a Just value, we can extract its associated value by pattern matching (which is what we did in the first implementation of safeLogSqrt). This is not true of all monads, who often help you by hiding unnecessary details. It is also perfectly possible to make a "no-exit" monad, from which it is never possible to extract "pure" values, such as the IO monad: in this way it is impossible not to notice that an operation involves input/output (and thereby side effects), because any I/O operation will carry around the IO monad, which functions as a warning sign.