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”

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.

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)