[英]Is there a `liftIO` equivalence for conduits?
I'm writing a conduit that is growing quite big, with nested monad transforms.我正在编写一个越来越大的管道,带有嵌套的 monad 转换。 It's a tedious work to lift
each and every yield
or await
call into the base conduitM
. lift
每一个yield
或await
调用lift
到基础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.更不用说每次添加或撤回转换层时,我都需要在每个可能的位置更改lift
的数量。
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.我正在寻找与liftIO
类似的功能,但它不是提升 IO 操作,而是将yield
或await
提升为基于ConduitM
的任意转换的单子,但我似乎找不到。 Is there a way to achieve something like this?有没有办法实现这样的目标?
Edit: in response to @BradleyHardy's answer, here I provide a concrete example:编辑:为了回应@BradleyHardy 的回答,我在这里提供一个具体的例子:
{-# 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?您将如何更改myK
以将ConduitM
放在堆栈顶部? 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.诚然,在这个特殊的例子中,使用MaybeT
过于复杂,但在我的实际(更大的)管道中, MaybeT
结构比例如递归要干净得多。
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. ConduitM
本身就是一个单子转换,并且,看着黑线鳕页呢,我们看到它定义为实例MonadState
, MonadReader
等,这应该表明,事实上模式意是要有ConduitM
在变压器堆栈的顶部,在这种情况下,您不需要将任何操作提升到其中。
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.事实上,它甚至为MonadBase定义了一个实例,该类允许您从位于堆栈liftBase
的 monad 中提升操作(使用liftBase
函数),因此如果重新排序您的堆栈意味着很难访问新的最重要的是, MonadBase
为您解决了这个问题。 If you have IO
at the bottom anyway though, this probably isn't very helpful.不过,如果您在底部有IO
,这可能不是很有帮助。
I would suggest that you try to reorder your transformer stack to put ConduitM
on top if possible.如果可能,我建议您尝试重新排序变压器堆栈以将ConduitM
放在顶部。
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.编辑:另一种选择是创建您自己的类MonadConduit
,它定义了广义的yield
和await
函数,并为ConduitM
和您在其上使用的所有其他转换器添加实例。 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.我认为更好的方法是使用包含的Data.Conduit.Lift
模块。 It is as simple as replacing all your runMaybeT
, runStateT
, etc. into runMaybeC
and runStateC
.它就像将所有runMaybeT
、 runStateT
等替换为runMaybeC
和runStateC
一样简单。 And voila, the ConduitM
is pushed to the top of the transformer stack.瞧, ConduitM
被推到了变压器堆栈的顶部。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.