Control.Monad.IfElse

Written by Jeff Heard on January 29th, 2009

There’s been some discussion on the mailing list about changing the type of Control.Monad.when.  I had a need a little while back for the old LISP anaphoric-if.  I also had a need for something that turned out to be in Control.Monad already — when (but I didn’t know it at the time).  Since then, I’ve been throwing together miscellaneous useful control flow mechanisms, and I’ve packaged them in Control.Monad.IfElse. In this post, I’ll talk a little bit more about how to use them and when.

First, there’s the anaphoric if, when, and conditional: aif, awhen, and acond.  By anaphoric, we mean to say that the result of the conditional test is used as part of the result.

awhen :: Maybe a -> (a -> m ())
aif :: Maybe a -> (a -> m b) -> m b -> m b
acond :: Monad m => [(Maybe a, a -> m ())] -> m ()

In all cases, if the value of the test is “Just a”, then a gets passed into a clause.  In the case of awhen, the clause takes a and returns an m () (in my case, usually IO or Render).  In the case of aif, there is a parameterless else clause that is evaluated upon a Nothing.  And finally, acond takes a list of pairs where the first element is being tested and the second is the clause that is evaluated for a on a “Just a” There are also lifted variants of these, awhenM, aifM, and acondM, where the Maybe a is encased in a monad.

In addition, the documentation isn’t generated for them, but there are a few operators that exist in the source.  I’ll update the doc to reflect them, but

(>>?) is a synonym for when

and

(>>=?) is a synonym for awhen

Usage for these is:

test >>? clause

atest >>=? (\a -> clause)

In addition, there are liftM2-ed logical and and or, (||^) and (&&^).

The rest are pretty well documented in the Haddock.  There is a cond and condM, which is like an if-else-if chain (and ncond and ncondM are unless else chains).  There’s a whenM counterpart to when, where the test is lifted into the same monad as the clause.  There’s whileM and untilM, which unfold their clauses until the test is False (or True in the case of until).  There’s return’, which is return, but strict in its argument.  And finally there’s “returning,” which is a substitute for “clause >> return a”

That’s most of the library.  Enjoy.

7 Comments so far ↓

  1. BMeph says:

    I think you don’t understand what “flip” does…

  2. Jeff Heard says:

    Fixed the post. Indeed I was looking at it backwards.

  3. Erik says:

    Isn’t the aif function just a specialization of the maybe function with a different order of the arguments?

  4. Jeff Heard says:

    Yes, absolutely. This library is merely a bunch of small utilities, not a major undertaking that introduces fundamentally new constructs. Up to you which you think is easier to read.

  5. Mathias Stearn says:

    I think it would be nice if the action in whenM and others was “M a” not “M ()”. Obviously since the action might not complete the return type would have to still be M () but the return argument could simply be ignored if it exists.

    I sometimes like to write my update functions to return the modified object. Something like: myUpdate :: IORef Foo -> (Foo -> Foo) -> IO Foo. Unfortunately to use this with when I have to say: when condition (myUpdate ref fooizer >> return ()). IMO the (>> return ()) part belongs in when, not the calling code.

  6. If you give explicit type signatures to the symbolic functions, then Haddock will show their documentation. Also for the functions I added, if you move the SPECIALIZE and INLINE pragma so they do not come between the Haddock and the type signatures, then those documentation will display as well. (This latter bug was introduced in Haddock 2.1 and wasn’t there in Haddock 2.0)

  7. Reid says:

    Of course, as you know, many of these functions have simple definitions in terms of standard library functions. But some might not be aware that awhen has a one-*word* definition:

    aword :: Monad m => Maybe a -> (a -> m ()) -> m ()
    aword = Data.Foldable.forM_

    Foldable generalizes forM_ and similar functions to any Functor from which we can extract a list of elements. Traversable has even more neat stuff.

Leave a Comment