
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 shorter 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! Tillmann