I have a producer:
p :: Producer Message IO r
.
I can process all messages using:
runEffect $ for p processMessage
where
processMessage :: Message -> Effect IO ()
.
How can I implement stateful processing using something like:
processMessage :: Message -> Effect (StateT MyState IO) ()
?
Short answer:
processMessage
is finerunEffect
returns StateT MyState IO ()
, you need to evaluate itLonger answer with a dummy example:
Your producer is locked into the IO
monad, you need to modify it to be either in MonadIO m
or in the explicit state monad.
import Control.Monad.State
import Pipes
type Message = Int
p :: MonadIO m => Producer Message m ()
p = each [1..10]
The signature of your processMessage
is already fine. I'm following your signature and adding some simple logic to exercise the IO and State functionality
processMessage :: Message -> Effect (StateT MyState IO) ()
processMessage msg = do
modify (+ msg)
liftIO (print msg)
Then the final step. runEffect :: Monad m => Effect m r -> m r
, if you substitute the m
with a concrete type, this ends up being runEffect :: Effect (StateT MyState IO) () -> StateT MyState IO ()
, meaning that you'll be left with the state monad which still needs to be executed. There's three variants for executing the state monad, runStateT
, evalStateT
and execStateT
. I chose execStateT :: StateT MyState IO () -> IO MyState
variant in this example, but choose whichever you need in your case.
main :: IO ()
main = do
st <- execStateT (runEffect $ for p processMessage) 0
putStrLn $ "End state: " <> show st