4
votes

So I have this line of code:

[Nothing] >>= \(Just x) -> [x]

which of course gives exception, because the pattern doesn't match Nothing.

On the other hand, this code gives a different result, []:

do
  Just x <- [Nothing]
  return x

As I see it, they should produce the same result, because do-blocks should be desugared into using (>>=) and return. But this is not the case, making do-notation a feature rather than a syntactic sugar.

I know that fail exists in the monad type class and I know that it is called when a pattern matching fails in a do-block, but I can't understand why it is a wanted behavior that should be different than using normal monad operations.

So my questions is - why should the fail method exist at all?

1
It's still syntactic sugar, the sugar is just a little bit sweeter than you were thinking.Daniel Wagner
If it really is a syntactic sugar, then how would it be desugared into using bind and return?Brrch
The Haskell Report has the definitive answer to that.Daniel Wagner

1 Answers

1
votes

Code such as

\(Just x) -> ...

denotes a function. There's only one way to use such a value: apply it to some argument. When said argument does not match the pattern (e.g. is Nothing) application is impossible, and the only general option is to raise a runtime error/exception.

Instead, when in a do-block, we have a type class around: a monad. Such class could, in theory, be extended to provide a behavior for such cases. Indeed, the designers of Haskell decided to add a fail method just for this case.

Whether the choice was good or bad can be controversial. Just to present another design option, the Monad class could have been designed without fail, and blocks such as

do ... 
   Just x <- ...
   ...

could have been forbidden, or made to require a special MonadFail subclass of Monad. Erroring out is also a choice, but we like to write e.g.

catMaybes xs = do Just x <- xs
                  return x
-- or
catMaybes xs = [ x | Just x <- xs ]

to discard Nothings from a list.