Mixing monadic and non-monadic parameters with Applicative

Posted on November 10, 2011

Today, I’ve wondered how to call when with a monadic expression as first parameter.

Usually, I write something similar to the following code:

main :: IO ()
main = do
    ex <- doesFileExist "/etc/passwd"
    when ex $ do
        print "Hi, there"

But, I’ve always wondered how to avoid binding the result of doesFileExist. Today, my colleague Patrick Michel and I came up with a solution which is general and nice. Although, it’s quite possible that it is not new. It simply wasn’t known to us.

main :: IO ()
main = do
    when <$> doesFileExist "/etc/passwd" <#> do
       print "Hi, there"

<#> is somehow the dual to <$>. The latter lifts its left argument into a Functor, Monad, Applicative, whereas the former — <#> — ‘unlifts’ its left argument. Because unlifting is not possible — at least not in general, <#> lifts its right argument, applies the right argument to the left argument with <*>, and joins the result.

import Control.Applicative
import Control.Monad

(<#>) :: (Monad m, Applicative m) => m (m a -> m b) -> m a -> m b
a <#> b = join $ a <*> pure b
infixl 3 <#>

Using that new operator <#> it is now possible to replace the following:

do
    x_0 <- m_0
    ...
    x_n <- m_n
    m x_0 .. x_n m_(n+1) .. m_(n+k)

with:

    m <$> m_0 <*> .. <*> m_n <#> m_(n+1) .. m_(n+k)

<$> starts the sequence of arguments, which are of a pure type, but are computed by a monadic action, and <#> ends the sequence of those arguments.

I’d be very interested to hear, which alternatives to <#> exist or whether it is already hidden in the libraries under a different name.