MonadFail
This page contains some notes and ideas that predate the official MonadFail Proposal (MFP) which can be found at
prime:Libraries/Proposals/MonadFail
The Basic Idea
The basic idea is to split the Monad
class into
class Applicative m => Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
m >> k = m >>= \_ -> k
return :: a -> m a -- still in Monad for historic reasons
return = pure
class Monad m => MonadFail m where
fail :: String -> m a
and adapt do
-desugaring accordingly.
See https://github.com/quchen/articles/blob/master/monad_fail.md for more details.
Misc Ideas
To aid transition, we can keep fail
in Monad
, and start out with
class Monad m => MonadFail m where
mfail :: String -> m a
(or maybe even require MonadPlus
, as fail _ = mzero
is a sensible default). This is a comparable situation as with post-AMP pure
/return
, where the now redundant return
(which is now an alias for pure
) is to be phased out into an ordinary top-level (non-method) function in the long term.
The MonadFail(mfail)
desugaring of do
could then be enabled via a language pragma {-# LANGUAGE MonadFail -#}
, allowing for have a future -XHaskell201x
to switch that feature on by default, while retaining -XHaskell2010
with the current old Monad(fail)
desugaring semantics.
Monad(fail)
could default to MonadFail(mfail)
via -XDefaultSignatures
History
In Haskell 1.4 (postscript) fail
was not part of the Monad
class. Instead there was a separate MonadZero
class containing the zero
operation whose purpose was to handle pattern-failures in do
-syntax (akin to what fail
does today). Here are the original Haskell 1.4 class definitions quoted from section "6.2.5 Monadic Classes":
class Functor f where
map :: (a -> b) -> (f a -> f b)
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
class (Monad m) => MonadZero m where
zero :: m a
class (MonadZero m) => MonadPlus m where
(++) :: m a -> m a -> m a
However, when Haskell 98 was drafted issues with irrefutable patterns lead to MonadZero
being folded into the Monad
class (but it doesn't seem to have been an unanimous nor easy decision back then).
The issue is highlighted by deconstructing a monadic action returning a single-constructor value:
f :: Monad m => m (a,b) -> m a
f m1 = do { x <- m1; return (fst x) }
g :: ??? m => m (a,b) -> m a
g m1 = do { (a,_) <- m1; return a }
h :: Monad m => m (a,b) -> m a
h m1 = do { ~(a,_) <- m1; return a }
Should ???
for g
be Monad
or MonadZero
? The single-constructor pattern match will never fail ("unfailable"), so zero
is never used. In fact, in Haskell 1.4 g
would only require Monad
, but requires the concept of `"unfailable" pattern matches (in addition to "irrefutable" pattern matches).