简体   繁体   English

管道是否有“liftIO”等价物?

[英]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每一个yieldawait调用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 操作,而是将yieldawait提升为基于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本身就是一个单子转换,并且,看着黑线鳕页呢,我们看到它定义为实例MonadStateMonadReader等,这应该表明,事实上模式意是要有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 ,它定义了广义的yieldawait函数,并为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 .它就像将所有runMaybeTrunStateT等替换为runMaybeCrunStateC一样简单。 And voila, the ConduitM is pushed to the top of the transformer stack.瞧, ConduitM被推到了变压器堆栈的顶部。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM