I'm writing a conduit that is growing quite big, with nested monad transforms. It's a tedious work to lift
each and every yield
or await
call into the base conduitM
. Not to mention every time I add or withdraw a layer of transformation, I need to change the number of lift
's at every possible location.
I was looking for a similar function as liftIO
, but instead of lifting an IO operation it should lift yield
or await
into an arbitrarily transformed monad based on ConduitM
, but I can't seem to find one. Is there a way to achieve something like this?
Edit: in response to @BradleyHardy's answer, here I provide a concrete example:
{-# LANGUAGE LambdaCase #-}
import Control.Monad as MON
import Control.Monad.IO.Class as MIO
import Control.Monad.Trans.Class as MTC
import Control.Monad.Trans.Maybe as MTM
import Data.Conduit as CDT
import System.IO as IO
stdinS :: Source IO String
stdinS = void . runMaybeT . forever $ do
(liftIO isEOF) >>= \case
True -> mzero
False -> (lift . yield) =<< (liftIO getLine)
myK :: Sink String IO ()
myK = void . runMaybeT . forever . runMaybeT $ do
a <- maybe (lift mzero) return =<< (lift . lift $ await)
b <- if a == "listen"
then maybe (lift mzero) return =<< (lift . lift $ await)
else mzero
liftIO . putStrLn $ "I heard: " ++ b
main :: IO ()
main = do
stdinS $$ myK
How would you change myK
to put ConduitM
on top of the stack? Admittedly in this particular example using MaybeT
is over-complication, but in my actual (much larger) conduit MaybeT
structure is much cleaner than eg recursions.
ConduitM
is itself a monad transformer, and, looking at the Haddock page for it, we see that it defines instances for MonadState
, MonadReader
, etc. This should suggest that in fact the pattern intended is to have ConduitM
at the top of the transformer stack, in which case you don't need to lift any operations into it.
In fact, it even defines an instance for MonadBase , the class which lets you lift operations from the monad which sits at the very base of the stack (using the liftBase
function), so that if reordering your stack means it's hard to access the new thing at the bottom, MonadBase
solves this for you. If you have IO
at the bottom anyway though, this probably isn't very helpful.
I would suggest that you try to reorder your transformer stack to put ConduitM
on top if possible.
EDIT: Another option would be to create your own class, MonadConduit
, which defines generalised yield
and await
functions, and add instances for ConduitM
and every other transformer you use on top of it. I think that this is less elegant than reordering your stack if possible though.
I think a better way is to use the included Data.Conduit.Lift
module. It is as simple as replacing all your runMaybeT
, runStateT
, etc. into runMaybeC
and runStateC
. And voila, the ConduitM
is pushed to the top of the transformer stack.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.