[Haskell-cafe] monad subexpressions

Claus Reinke claus.reinke at talk21.com
Fri Aug 3 12:12:13 EDT 2007


to illustrate why some of us are concerned about this extension,
a few examples might help. consider:

    f (g (<- mx))

does this stand for 

    (a) mx >>= \x-> f (g x)
    (b) f (mx >>= \x-> (g x))
    (c) none of the above, because there's no do
    (d) something else entirely

if (a/b), does the decision depend on the type of g (if g is pure,
then (a), if g is monadic itself, then (b))? if (d), what?

if (a/b), then this is no longer preprocessing, but depends on the
types, which means that the type system must work in the presence
of this extension, rather than its pre-processed form. if you want to
avoid that, you're left with (c), but is that any better?

if (c), then the following are no longer equivalent

    1. return ...
    2. do return ...

in particular, 

    do return ..

is no longer a unit of the monad (in (a/b), even return .. isn't). so
if you write

    f (do g (<- mx))

you mean (b), while if you write 

    do f (g (<- mx))

you mean (a), and if you write 

    f (g (<- mx))

you mean either an error, if there is no surrounding 'do', or something 
else, if there is a surrounding 'do'. and woe to those who think they can 
insert some 'do' notation whereever they like - they need to check the 
subexpressions for (<-) first!

now, consider nesting monadic subexpressions:

    f (<- g (<- mx))

surely means the same as f =<< (g =<< mx), namely 

    mx >>= \x-> g x >>= \gx-> f gx

right? wrong! we forgot the 'do'. without a 'do'-context, this means
nothing in (c). so if you have

    do 
        ..
        fx <- f (<- g (<- mx))
        ..
        fx <- f (<- g (<- mx))
        ..

and there are no free variables, then you can do the usual sharing to
improve readability, right?

    let fgmx = f (<- g (<- mx)) in
    do 
        ..
        fx <- fgmx
        ..
        fx <- fgmx
        ..

wrong again! this is syntax, not expression, so the latter variant
changes the scope the (<-)s refer to (some outer 'do', if one exists).
you could have written 

    do 
        let fgmx = f (<- g (<- mx))
        ..
        fx <- fgmx
        ..
        fx <- fgmx
        ..

perhaps, and at this stage you might no longer be surprised that
do and let no longer commute. or were you? if you weren't, here's
a quick question: we've already seen the left- and right-identity
laws in danger, so what about associativity?

    do { do { a; b}; c } 

is still the same as
            
    do { a; do { b; c } }

yes? no? perhaps? sometimes? how long did it take you?

could someone please convince me that i'm painting far too
gloomy a picture here?-) 

claus



More information about the Haskell-Cafe mailing list