One of the great features provided by Pipes package is the ability to utilize monad transformers in isolated sections of a pipeline. Conduit historically has not had this advantage because it did not quite follow the monad transformer laws..
Data.Conduit.Lift
gives a method to get around this limitation so that Conduit can utilize monad transformers in isolate sections of conduit much like the Pipes.Lift
module provides for Pipes. It is also a rather mechanical translation of the work I recently provided for Pipes.Lift
. The technique seems to be quite general and applies to both Pipes, Conduit and probably other streaming systems as well.
Here is an example fro the link above demonstrating the problem with running the state transformer in an isolate section of conduit.
{-# LANGUAGE OverloadedStrings #-}
import Data.Conduit
import qualified Data.Conduit.List as CL
import Control.Monad.State
import Control.Monad.Trans.Class
source :: Source IO ()
source = CL.sourceList $ replicate 10 ()
replaceNum :: Conduit () (StateT Int IO) Int
replaceNum = awaitForever $ \() -> do
i <- lift get
lift $ put $ i + 1
yield i
main :: IO ()
main = do
x <- source $$ transPipe (flip evalStateT 1) replaceNum =$ CL.consume
print x
y <- flip evalStateT 1
$ transPipe lift source $$ replaceNum =$ CL.consume
print y
Data.Conduit.Lift
provides evalStateC to handle this evaluating the state transformer locally rather then globally along the entire conduit:
test = do
x <- source3 $$ evalStateC 1 replaceNum =$ CL.consume
print x
> test
[1,2,3,4,5,6,7,8,9,10]
This has several advantages:
- Conduit sections that need to eval a state transform can be implemented and used with out having to worry about adding an evalStateT outside the conduit
- Monad transformers can be isolated to only where they are needed. If you need state it no longer needs to be global to the entire conduit.
Data.Conduit.Lift
implements functionality for the:
ErrorT
MaybeT
ReaderT
StateT
WriterT
rwsT
This technique will work for any monad transformer you can write a MFunctor
instance for.
I hope this makes it easier to include your favorite monad transformer in your Conduit code or vs versa.