[Haskell-cafe] Parsers are monadic?

Claus Reinke claus.reinke at talk21.com
Mon Jul 2 07:16:33 EDT 2007


>> contrary to monadic parsers, those continuation-based parsers
>> had *two* continuations, one for success, one for failure. and
>> that seemed to be a very natural match for the problem.
>
>Two-continuations is a monad too, right?

yes, but my problem is not about giving them a monadic interface,
but about getting more advantages than disadvantages out of that.
the initial definitions are more complicated than using continuations
directly, and usually, the monadic interface forces me to interleave 
handling of the two paths into a single thread of specifications. 

and while instances of the monadic combinators can be defined to 
thread information for both continuations, i still have to inject that
information outside the monadic interface, and make sure that it
doesn't get reset to nothing just because someone calls Monad
fail (via a library function or pattern-match failure), or MonadPlus 
mzero (via things like guard).

a second problem is that some of the advantages of monadic 
interfaces that i tend to rely on heavily, such as pattern-match
failure handling in do-notation, are hardwired to the wrong part
of the monadic interface (Monad fail). this is not something a 
non-monadic approach would offer any help with, though..

> instance Monad m => Monad (ContErrorT m) where ..
> instance Monad m => MonadError Exception (ContErrorT m) where ..
>
> Am I missing something really obvious here?

interesting. i tend to use Monad/MonadPlus for sequence/
alternative, rather than Monad/MonadError, but you're right: 
MonadError's method types are a more natural dual to Monad's.
they don't have to be limited to Exception, either.

it seems i should be using that class more. actually, i am using
that class, via the ErrorT transformer, but i guess i thought of 
MonadError only as error handling in sequences (which is the
way it is usually presented) rather than as fallthrough in alternatives.

it would force me to decide in advance whether i want fallthrough 
branches (MonadError) or collections of alternatives (MonadPlus), 
whereas MonadPlus allowed me to delay that decision (using 
MonadPlus Maybe for fallthrough or MonadPlus [] for collections, 
for instance). hmm, perhaps one can abstract over that decision.

now, if we could replace calls to Monad fail (especially, pattern
match failure in do-notation) with calls to MonadError throwError,
we might actually be getting somewhere (similarly, we'd need to
replace MonadPlus-based guard with a MonadError-based 
variant). as a start, 

    fail msg = throwError (PatternMatchError msg)

isn't too far off, but it would be lying for fail called from library 
functions, not to mention that it would still be limited to Strings.
but i've already used my own variant of catchError to limit
information loss and preserve the most specific error message,
so once again, MonadError seems a natural fit.

all in all, your answer only strengthens my view that monadic
programming would be more symmetric if we always had two
continuations, one for failure, one for success. then do-notation
translation could rely on both Monad and MonadError, using
throwError instead of fail (it has been suggested to use mzero,
with a separate MonadZero class, but MonadError may well
be the better match).

perhaps there'll come a time when the monadic aspects of 
haskell will need redesign, similar to the numeric aspects.

thanks! this emphasized view of MonadError might help.
claus
 


More information about the Haskell-Cafe mailing list