*** I was originally posting to ask a question, but I've solved the
*** question through trying to ask it. :-) I've included the mail
*** anyway in case anyone finds it useful.


|The fixity only makes a difference when you consider an expression
|like  f >>= g >>= h, where, for example, f,g,h are variables.

A while ago I tried to work out how to easily "drop out" of a monad
(IO monad in this case) but only partially succeeded.  It seemed to me
at the time that I could have solved it were I to use 1.3 fixity of >>
but perhaps someone else can solve it for 1.4/98 fixity?  Here's the
problem:

Suppose you have a sequence of commands, some of which may "fail" in
some way, meaning that the whole sequence should stop and do something
else.  Whether to stop or not depends on the results of these
commands.  Is there any way to write a simple function to "drop out"
of this sequence according to some test?

Here's an example:

> main :: IO ()
> main = f >>= \success ->
>        dropOut (not success) (
>           g >>= \failure ->
>           dropOut failure (
>              h))
>
> dropOut :: Bool -> IO () -> IO ()
> dropOut gotError cont | gotError  = return ()
>                       | otherwise = cont

As you can see, this starts to build up brackets (and indentation if
you follow convention).  It's not very neat...  fortunately I can use
$ to avoid the brackets:

> main :: IO ()
> main = f >>= \success ->
>        dropOut (not success) $
>        g >>= \failure ->
>        dropOut failure $
>        h
>
> dropOut :: Bool -> IO () -> IO ()
> dropOut gotError cont | gotError  = return ()
>                       | otherwise = cont

All well and good...  but what if I don't want the value from a
command, and so use >> instead?  There are now no lambdas to help me
out, and so the following:

> main :: IO ()
> main = f >>
>        dropOut cond1 $
>        g >>
>        dropOut cond2 $
>        h
>
> dropOut :: Bool -> IO () -> IO ()
> dropOut gotError cont | gotError  = return ()
>                       | otherwise = cont

will not work because the precedence of $ is lower than that of >> and
so this parses under 1.4/98 fixity of >> as:

> main :: IO ()
> main = (f >>
>         dropOut cond1) $
>        ((g >>
>         dropOut cond2) $
>         h)
>
> dropOut :: Bool -> IO () -> IO ()
> dropOut gotError cont | gotError  = return ()
>                       | otherwise = cont

whereas under 1.3 fixity it parses as:

> main :: IO ()
> main = f >>
>        (dropOut cond1 $
>         (g >>
>          (dropOut cond2 $
>           h)
>
> dropOut :: Bool -> IO () -> IO ()
> dropOut gotError cont | gotError  = return ()
>                       | otherwise = cont

which is what we want.

Of course, I can introduce dummy variables:

> main :: IO ()
> main = f >>= \_ ->
>        dropOut cond1 $
>        g >>= \_ ->
>        dropOut cond2 $
>        h
>
> dropOut :: Bool -> IO () -> IO ()
> dropOut gotError cont | gotError  = return ()
>                       | otherwise = cont

which works, though is debatably a slight hack.  However, one will
only usually want to "drop out" upon receiving a bad result from a
previous command, so perhaps this case does not appear very often.

Hmm, perhaps this is fine after all. :-)

Graeme.



Reply via email to