On Code etc.

1 note

data Maybe — harmful?

Here’s a question: is overemphasis of the Maybe type actually harmful, making it easier for Haskell newcomers to write unreliable code?

In some recent Haskell projects, I relied heavily on the Maybe type. It is simple to understand, Maybe is often the first Monad people learn and one of the first places that people start exploring Haskell’s power (realizing you can use do notation with it was a pretty cool moment for me). It is often the first major focal point to Haskell tutorials. And so it’s not surprising that I’ve used it a lot (I bet many people have).

Now I definitely don’t think that Maybe is not useful sometimes, and here’s a good example: looking for an item in a list. It is either there (Just value) or not there (Nothing). What is important about this example is that it is that both are normal, expected results. But what about the case when you are finding a value in a list but it should definitely be there (let’s say you put it in the list, serialized to disk, read it back in, and are inspecting the list), in that case the two possibilities are not equally likely, and passing back a Nothing value might be hiding some underlying problem.

What I noticed about my code is that I had started using the Maybe monad for failure conditions, or in cases where I really only expected the value to be a Just, but it was so easy to use Nothing that I ended up writing code that type checked (and compiled, and ran), but that provided virtually no information about errors that were occurring, or where they occurred. Part of this ease is the way you can use Maybe as a Monad: comp1 >>= comp2 >>= comp3 is so simple and clean, hiding within it that comp1 can genuinely return either a value or not, but comp2 and comp3 should really only not return a value in the case of something being wrong. If you end up with Nothing at the end of this, you really have no idea what actually went wrong, if anything.

Code written this way is difficult to debug once you find a bug, and good at hiding bugs in the first place (because we don’t know if the the result is Nothing because the item in the database or wherever didn’t exist, or because it was formatted incorrectly or because something else happened that shouldn’t have).

What I realized, which is probably obvious to any experienced Haskell programmer, is that Maybe should not ever be used in cases where an error has occurred. There are (at least) two ways of properly handling errors: the first being the Either type, which is like Maybe if Nothing carried a type with it (so you have either Left error-value or Right success-value), or if it is indeed an error that means things are really messed up (and should not keep going), error - a function that causes a runtime error to be raised (that can be caught, but if not, causes the program to exit).

Especially in web programming (where everything I’ve done recently is), calls to error can (and at least with snap, do) cause the request to terminate and a 500 to be sent to the client, which in the case of an error that can not be recovered from, is probably desired! In most other cases, Either is probably a practical solution, as it allows you to fail in the same way as Nothing, but specify where it happened, and maybe some other details. And it can be used in the Monadic style if you import Control.Monad.Error.

So my conclusion with all of this is to only use Maybe when a value can truly be there or not be there, not when it should be there and it’s absence is an error. And, to be careful about using library functions that return Maybe values if in my case they should only not return values in exceptional cases. I’d be curious to know what more experienced Haskell programmers think about Maybe, and whether they’ve come up with different solutions to the problems I’ve run into.

Filed under haskell

  1. dbpatterson posted this