[Haskell-cafe] Stacking monads

Tillmann Rendel rendel at daimi.au.dk
Fri Oct 3 21:16:20 EDT 2008

Andrew Coppin wrote:
>  run_or s0 x y =
>    let
>      either_rset1 = sequence $ run s0 x
>      either_rset2 = sequence $ run s0 y
>      either_rset3 = do rset1 <- either_rset1; rset2 <- either_rset2; 
> return (merge rset1 rset2)
>  in case either_rset3 of
>    Left  e    -> throwError e
>    Right rset -> lift rset

Just to expand on that discussion of Control.Monad.ap aka. 
(Control.Applicative.<*>) in the other half of the thread. The expression

   do rset1 <- either_rset1
      rset2 <- either_rset2
      return (merge rset1 rset2)

follows exactly the pattern Applicative is made for: We execute some 
actions, and combine their result using a pure function. Which action we 
execute is independent from the result of the previous actions. That 
means that we can write this expression as:

   return merge `ap` either_rset1 `ap` either_rset2

Note how we avoid to give names to intermediate results just to use them 
in the very next line. Since return f `ap` x == f `fmap` x, we can write 

   merge `fmap` either_rset1 `ap` either_rset2

Or in Applicative style:

   merge <$> either_rset1 <*> either_rset2

Now that the expression is somewhat shorter, we can inline the 
either_rset1, 2 and 3 as follows:

   case merge <$> sequence (run s0 x) <*> sequence (run s0 y) of
     Left  e    -> throwError e
     Right rset -> lift rset

Note how the structure of the code reflects what happens. The structure 
is merge <$> ... <*> ..., and the meaning is: merge is called on two 
arguments, which are created by running some actions, and the result is 
again an action.

While we are one it, we can get rid of the pattern matching by employing 
the either function as follows:

   either throwError lift (merge <$> sequence (run s0 x) <*> sequence 
(run s0 y))

> Do you realise, this single snippet of code utilises the ErrorT monad [transformer], the ResultSet monad, *and* the Either monad, all in the space of a few lines?? That's three monads in one function! o_O 

Now it fits on a single line!


More information about the Haskell-Cafe mailing list