'Should I avoid using Monad fail?
I'm fairly new to Haskell and have been slowly getting the idea that there's something wrong with the existence of Monad fail. Real World Haskell warns against its use ("Once again, we recommend that you almost always avoid using fail!"). I just noticed today that Ross Paterson called it "a wart, not a design pattern" back in 2008 (and seemed to get quite some agreement in that thread).
While watching Dr Ralf Lämmel talk on the essence of functional programming, I started to understand a possible tension which may have led to Monad fail. In the lecture, Ralf talks about adding various monadic effects to a base monadic parser (logging, state etc). Many of the effects required changes to the base parser and sometimes the data types used. I figured that the addition of 'fail' to all monads might have been a compromise because 'fail' is so common and you want to avoid changes to the 'base' parser (or whatever) as much as possible. Of course, some kind of 'fail' makes sense for parsers but not always, say, put/get of State or ask/local of Reader.
Let me know if I could be off onto the wrong track.
Should I avoid using Monad fail? What are the alternatives to Monad fail? Are there any alternative monad libraries that do not include this "design wart"? Where can I read more about the history around this design decision?
Solution 1:[1]
In Haskell 1.4 (1997) there was no fail. Instead, there was a MonadZero type class which contained a zero method. Now, the do notation used zero to indicate pattern match failure; this caused surprises to people: whether their function needed Monad or MonadZero depended on how they used the do notation in it.
When Haskell 98 was designed a bit later, they did several changes to make programming simpler to the novice. For example, monad comprehensions were turned into list comprehensions. Similarly, to remove the do type class issue, the MonadZero class was removed; for the use of do, the method fail was added to Monad; and for other uses of zero, a mzero method was added to MonadPlus.
There is, I think, a good argument to be made that fail should not be used for anything explicitly; its only intended use is in the translation of the do notation. Nevertheless, I myself am often naughty and use fail explicitly, too.
You can access the original 1.4 and 98 reports here. I'm sure the discussion leading to the change can be found in some email list archives, but I don't have links handy.
Solution 2:[2]
I try to avoid Monad fail wherever possible, and there are a whole variety of ways to capture fail depending on your circumstance. Edward Yang has written a good overview on his blog in the article titled 8 ways to report errors in Haskell revisited.
In summary, the different ways to report errors that he identifies are:
- Use error
- Use Maybe a
- Use Either String a
- Use Monad and fail to generalize 1-3
- Use MonadError and a custom error type
- Use throw in the IO monad
- Use ioError and catch
- Go nuts with monad transformers
- Checked exceptions
- Failure
Of these, I would be tempted to use option 3, with Either e b if I know how to handle the error, but need a little more context.
Solution 3:[3]
If you use GHC 8.6.1 or above, there is a new class called MonadFail and a language extension called MonadFailDesugaring. If you have access to these, use them. You can then use fail without a problem that some monad actually has no fail implementation.
This extension makes the fail desugaring requires a more restrictive MonadFail instead of any monad.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ry- |
| Solution 2 | Nicolas Wu |
| Solution 3 | Xwtek |
