Search code examples
haskellfunctional-programmingmonadsstate-monadmonoids

Is there a Monad which collects results and `mappend`s them?


I have the following pattern of a Reader with a Semigroup Element:

runFunction :: Reader Env Element
runFunction = do
  a <- getA
  b <- getB
  c <- getC
  return $ a <> b <> c

Where getA :: Reader Env Element.

Is there a way to:

runFunction = do
  getA
  getB
  getC

I feel like I see this pattern alot, where I imperatively chain monadic calls, and they get turned into a single element at the end.

Note: I don't want to do getA >>= getB >>= getC since getB isn't :: Element -> Reader Env Element

It feels like a State Monad, that automatically modifies state with <>, but I don't know.

Working with monadic code is still quite fresh to me.


Solution

  • The WriterT monad transformer can be used to build up a monoidal value with each action.

    You can use lift to wrap your Reader actions into the WriterT monad transformer, then move the result of each action into the monad's environment by using tell, sequence the actions, and then unwrap the result back to a single Reader action using execWriterT.

    Here is what I mean:

    import Control.Monad.Writer
    
    wrap :: (Monad m, Monoid w) => m w -> WriterT w m ()
    wrap m = lift m >>= tell
    
    unwrap :: (Monad m) => WriterT w m a -> m w
    unwrap = execWriterT
    
    runFunction :: Reader Env Element
    runFunction = unwrap $ do
        wrap getA
        wrap getB
        wrap getC
    

    As you can see, this generalizes to any monad, not just Reader.