Maybe I Should Be In The Maybe Monad
If you’ve spent any time with Haskell, then you’ve surely encountered
Maybe is Haskell’s version of testing your pointer for
NULL, only its better because it’s impossible to accidentally dereference a
You’ve also probably thought it was just so annoying. You test your
Maybe a to ensure it’s not
Nothing, but you still have to go about getting the value out of the
Maybe so you can actually do something with it.
Let’s forgo the usual contrived examples and look at an actual problem I faced. While working on the Server Console, I was faced with dealing with a query string. The end of a query string contains key/value pairs. Happstack conveniently decodes these into this type:
I needed to write a function to lookup a key within this pair, and return it’s value. As we all know, there’s no way to guarantee that a given key is in the list, so the function must be able to handle this. There are a few ways we could go about this, but this seems to me to be an ideal place to use a
Maybe. Suppose we write our lookup function like so:
lookup :: Request -> String -> Maybe String
This is logically sound, but now we have an annoying
Maybe to work with. Suppose we’re working in a
ServerPart Response. We might write a response function like so:
handler :: ServerPart Response handler = do req <- askRq paths <- return $ rqPaths req page <- return $ lookup req "page_number" case page of Nothing -> mzero (Just a) -> do items <- return $ lookup req "items_per_page" case items of Nothing -> mzero (just b) -> h' paths a b
Yucky! After each call to lookup, we check to see if the call succeeded. This gives us a giant tree that’s surely pushing off the right side of my blog page. There must be a better way.
Doing It Wrong
Shockingly, this is not the best way to do this. It turns out that writing our functions in the
Maybe monad is the answer. Take the following function:
hTrpl :: Request -> Maybe ([String], String, String) hTrpl r = do paths <- return $ rqPaths r page <- lookup r "page_number" items <- lookup r "items_per_page" return (paths, page, items)
… now we can re-write
handler like so:
handler :: ServerPart Response handler = do req <- askRq triple <- return $ hTrpl req case triple of Nothing -> mzero (Just (a, b, c)) -> h' a b c
Much better, right? But why don’t we have to test the return values of
lookup? The answer to that question lies in the implementation of
instance Monad Maybe where (Just x) >>= k = k x Nothing >>= _ = Nothing
do notation is just syntactic sugar around
>>= and a whole bunch of lambdas. With that in mind, you can see that we are actually binding functions together. Per
Maybe‘s bind implementation, if you bind
Just a to a function, it calls the function on
a. If you bind
Nothing to a function, it ignores the function, and just returns
What this means for us is that so long as we’re inside the
Maybe monad, we can pretend all functions return successful values.
Maybe allows us to defer testing for failure! The first time a
Nothing is returned, functions stop getting called, so we don’t even have to worry about performance losses from not immediately returning from the function! So long as we’re inside of
Maybe, there will be Peace On Earth. We code our successful code branch, and then when all is said and done and the dust has settled, we can see if it all worked out.
Next time you find yourself testing a
Maybe more than once in a function, ask yourself: should I be in the
Maybe monad right now?