Haskell Conduit:可以选择性地获得源的结果吗?

时间:2015-02-15 07:34:01

标签: haskell conduit

我从Data.Conduit构建了以下类型:

type Footers = [(ByteString, ByteString)]

type DataAndConclusion = ConduitM () ByteString IO Footers

第二种类型的想法是“产生大量ByteStrings,如果你可以产生所有这些,返回一个页脚”。条件是因为管道由下游功能管理,因此DataAndConclusion的使用者可能不需要消耗其所有项目,并且在这种情况下将不会达到返回。这正是我需要的行为。但到达源代码的结尾时,我想拥有生成的Footers。这将是有用的,例如,如果DataAndConclusions正在递增地计算MD5,并且只有在整个消息由下游处理时才需要这样的MD5(例如,下游可能只是通过网络发送它,但它没有意义如果套接字在下游发送最后一块之前关闭,则完成计算并发送MD5。

所以,基本上我想要使用此签名来使用DataAndConclusions:

 type MySink = Sink ByteString IO ()

 mySink :: MySink 
 mySink = ... 

 difficultFunction :: ConduitM () a2 m r1 -> ConduitM a2 Void m r2 -> m (Maybe r1)

问题是,有没有办法实现“hardFunction”?怎么样?

2 个答案:

答案 0 :(得分:2)

  

如果DataAndConclusions是,那么这将非常有用   逐步计算MD5,只有在需要时才需要这样的MD5   整个邮件由下游处理

在这种情况下,您可以在StateT下的ConduitM层中累积正在进行的MD5计算,而不是依赖上游管道的返回值,并在运行管道后访问它。 / p>

谜题的另一部分是检测到生产者已经完成了第一次。 Sink可以检测await次呼叫中的上游输入结束。您可以编写Sink来通知您自己的结果类型中的上游终止,可能使用Maybe

但是如果给你一个Sink还没有这样做呢?我们需要像Sink i m r -> Sink i m (Maybe r)这样的函数。 “如果Sink可能提前停止,则返回一个新的Sink,如果上游完成,则返回Nothing”。但我不知道如何写这个功能。

修改:此渠道在检测到上游终止时将IORef设置为True

detectUpstreamClose :: IORef Bool ->  Conduit i IO i
detectUpstreamClose ref = conduit
  where
    conduit = do
        m <- await
        case m of
          Nothing -> liftIO (writeIORef ref True)
          Just i -> do
              yield i
              conduit
可以在管道中插入

detectUpstreamClose,之后可以检查IORef

答案 1 :(得分:2)

应该有一个很好的解决方案,但我无法使用ConduitM原语构建它。带签名的东西

ConduitM i a m r1 -> ConduitM a o m r2 -> ConduitM i o m (Maybe r1, r2)

看起来像带有此签名的原始函数将是管道库的一个很好的补充。

尽管如此,@ danidiaz关于StateT的建议引导我采用以下通用解决方案,将整个计算内部提升到WriterT,以便记住第一个管道的输出,如果达到:

import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Writer
import Data.Conduit
import Data.Monoid
import Data.Void

difficultFunction :: (Monad m)
                  => ConduitM () a2 m r1 -> ConduitM a2 Void m r2
                  -> m (r2, Maybe r1)
difficultFunction l r = liftM (fmap getLast) $ runWriterT (l' $$ r')
  where
    l' = transPipe lift l >>= lift . tell . Last . Just
    r' = transPipe lift r

(未经测试!)