maybe Maybe is not so bad

I’m having some FUN with FP101x by Erik Meijer on the edX plattform and watched the lectures on the countdown problem yesterday.

Now I think Erik has very strong opinions and I really like this – most of the time – but there he once again told us about his mislike of Maybe and why list are just nicer.

He did not name any more reasons besides that lists are just more elegant to deal with in Haskell and I assume he is talking about list-comprehensions that let us write the code indeed very elegantly (have a look at the original paper).

But wait – we have Monad comprehensions back, and having these, all I had to do was change 4 lines of the provided template in the course to get the exact same results:

  • Add the extensions to the top of the file: > {-# LANGUAGE MonadComprehensions #-}
  • change the type of the eval function to eval :: Expr -> Maybe Int
  • write in the implementation of the solution function ... eval e == return n instead of eval e == [n] (and this works for lists too)
  • the very same for the solutions function

You can find the changed template here.

So if we set aside the extension and the two things that would work for lists too, the only difference is the type of eval.

I cannot see where Maybe makes this code less elegant – in my opinion it makes the code/types more expressive.

If you don’t like the extensions you could instead use classical do notation and guard. So instead of

eval                          :: Expr -> [Int]
eval (Val n)                  =  [n | n > 0]
eval (App o l r)              =  [apply o x y | x <- eval l
                                              , y <- eval r
                                              , valid o x y]

you write

eval                          :: Expr -> Maybe Int
eval (Val n)                  = do
                                guard (n > 0)
                                return n
eval (App o l r)              = do
                                x <- eval l
                                y <- eval r
                                guard (valid o x y)
                                return $ apply o x y

That again would work for both Maybe Int and [Int] just at the cose of 1 or 2 additional lines (do/return) – maybe not as elegant but still – no big deal is it?

what would we gain

So why would I go with Maybe over lists here?

In my opinion Maybe just tells you more about the result: it can either succeed or fail – it marks partial functions really nicely.

Lists do more: they include the option to have more than one result and represent non-determinism. I don’t think we really need or want this here.

what about the remark Erik made

Erik made a remark that you have to get rid of Maybe in the end – and of course this is true – you have to deal with the two cases finally. The same is true for lists (you have to wrap values just like we did with return n vs [n] above, and have to unwrap them for example with head) – and just like for lists there are many functions that helps you with this in the prelude. Surely that is no reason to not let the compiler/typesystem help you find even more bugs, or is it?