[Haskell] A collection of related proposals regarding monads

Cale Gibbard cgibbard at gmail.com
Wed Jan 4 15:54:51 EST 2006


I personally feel that the inclusion of 'fail' in the Monad class is
an ugly solution to the problem of pattern matching, and gives the
incorrect impression that monads should have some builtin notion of
failure. Indeed, it's becoming common to type the result of some
function in an arbitrary monad in order to indicate the potential for
failure, which is strictly speaking, not the right thing to do. (In a
lot of cases, it's going to be no better than complete program
failure)

We ought to be using MonadZero when we want to express failure, but it's gone!

What do people think of the following proposal? Remove fail from the
Monad class. Reinstate MonadZero as a separate class as in Haskell
1.4.

Do notation is translated as in Haskell 1.4:
do {e} = e
do {e;stmts} = e >> do {stmts}
-- if p is refutable,
do {p <- e; stmts} = let ok p = do {stmts}; ok _ = mzero in e >>= ok
-- if p is irrefutable,
do {p <- e; stmts} = e >>= \p -> do {stmts}
do {let decls; stmts} = let decls in do {stmts}

Thus, refutable pattern matches occuring in a do-expression would
force the expression to be typed explicitly with MonadZero. This is
probably a good thing, as it forces one to think about the
consequences of not properly dealing with a pattern match in any monad
which doesn't explicitly allow for failure.

Even if this translation of do-syntax isn't accepted, I still think
that we should have a separate MonadZero. Its existence lets various
type signatures become more expressive. There are a lot of cases where
a MonadPlus constraint pops up where a MonadZero constraint would do.
(I've also been seeing Monad get used for these cases, when it
shouldn't!) This distinction allows one to see immediately when a
monad is getting used for failure or whether choice is really
essential.

I'd also like to see the current use of MonadPlus split into MonadElse
(or MonadOr) and MonadPlus, as described at the bottom of
http://www.haskell.org/hawiki/MonadPlus
as it helps to clarify the distinction between backtracking-type
failure and immediate failure in types. We could even put this
distinction to good use in many monads which do support backtracking
anyway:

instance MonadElse [] where
    [] `morelse` ys = ys
    (x:xs) `morelse` ys = (x:xs)

Lastly, it would be nice to have some standard name for the function:
option :: (MonadPlus m) => [a] -> m a
option = foldr (mplus . return) mzero
which seems to come up quite a bit in my experience with nondet monads.

 - Cale

P.S. Oh, and don't get me started about the whole Functor subclass
thing, and the inclusion of join in the Monad class. Of course I want
those too. :)

P.P.S. This reopens the possibility for monad comprehensions, and the
more general versions of filter, concat, etc. in the 1.4 prelude -- I
wasn't actually around for Haskell 1.4, but I *really* think they'd be
a nice language feature to have, as I like to view monads as an
abstraction of *containers*, which that syntax/methodology emphasises.
I usually find that initially teaching monads to newcomers in terms of
containers is much more effective, and that syntax would allow for a
nice segue from lists to monads. If people think the error messages
are scary, we could provide a switch -fbeginner and a more 98-like
prelude to turn monad comprehensions and any other potentially scary
features off for the newcomers.


More information about the Haskell mailing list