
Hi This topic comes up a lot, and this is what I usually say when it does. It's a thing I learned from James McKinna, many years ago... Might I gently suggest that there is a much better, more natural way to abstract over every type-former which has some sort of return/pure-like thing and some sort of mzero/empty like thing? You could use the type-former which is inductively defined to be the least such thing, and as such has a canonical mapping to all the others, namely Maybe. It's not necessarily a good idea to fix on Monad or MonadPlus as there are other choices. For example, On 15 Sep 2009, at 07:14, Yusaku Hashimoto wrote:
I prefer Alternative to MonadPlus for explaining failure. It has better name and operator for failure and try-another.
import Control.Applicative
aLookup :: (Alternative f, Eq k) => k -> [(k,v)] -> f v aLookup key pairs = maybe empty pure $ lookup key pairs
there are notorious non-monadic instances for the above f (some formulations of parsing, in particular). So,
I understand that fail being in Monad is controversial, but my version of the function works in *all* monads.
this is a touch presumptuous. On the one hand, Brent is right when he says
It doesn't work in *all* monads -- it only works in monads which support a sensible notion of failure.
but he's perhaps excessive when he says
This is exactly what is captured by the MonadPlus constraint on my version of mLookup.
because it's not exact: it requires mplus as well as a sensible notion of failure. And yes, why should we insist on (>>=) when we just need a return and an mzero? Incidentally, I don't know where the MonadPlus instance
(IO, Maybe, [], ...) are already instances of MonadPlus.
of IO is coming from, but I want it caught and locked up now (in STM, for example) before it does any permanent damage. Why not factor out the failure-prone operations from the business of interpreting failure in some failure-supporting context? Work concretely while you can (types stay shorter, error messages make more sense) then apply adapters malt :: Alternative f => Maybe x -> f x malt = maybe empty pure mop :: MonadPlus m => Maybe x -> m x mop = maybe mzero return when you need to? This also reduces the risk of connecting an ambiguous supplier to an ambiguous consumer, (show . read) style. The message clearly bears repeating. Inductive definition is a concrete form of abstraction. Don't be fooled by its appearance: Maybe is the most abstract choice here -- the classier options demand more structure than is needed and thus exclude use-cases. I'll crawl back under my stone now. All the best Conor