将一个恢复源附加到另一个恢复源



当一个ResumableSource不是明确的Monad实例时,如何将它们附加到另一个?下面是一个玩具示例 -aMonad约束,而b没有约束。所以我们可以附加a's但不能附加b's

Prelude>  import Data.Conduit
Prelude Data.Conduit> import Data.ByteString as BS
Prelude Data.Conduit BS> import Control.Monad.Trans.Resource
Prelude Data.Conduit BS Control.Monad.Trans.Resource> let a = newResumableSource (yield (BS.pack [5])) -- this one has monad constraint
Prelude Data.Conduit BS Control.Monad.Trans.Resource> :t a
a :: Monad m => ResumableSource m ByteString
Prelude Data.Conduit BS Control.Monad.Trans.Resource> :t a >> a
a >> a
:: (Monad m, Monad (ResumableSource m)) =>
ResumableSource m ByteString
Prelude Data.Conduit BS Control.Monad.Trans.Resource> let b = undefined :: ResumableSource (ResourceT IO) ByteString
Prelude Data.Conduit BS Control.Monad.Trans.Resource> :t b >> b
<interactive>:1:3:
No instance for (Monad (ResumableSource (ResourceT IO)))
arising from a use of ‘>>’
In the expression: b >> b

我之所以问它,是因为我有一个与上面b类型相同的HTTP ResumableSource,我想在将其放入下沉之前预先添加内容长度。目前,它看起来像这样:

responseBody rsp  $$+- sink

我想改成这样:

((newResumableSource (yield content-len)) >> (responseBody rsp))  $$+- sink

将初始消息附加到ResumableSource的一个好方法似乎是使用产生初始消息然后成为传递的conduit。在这里,我从map管道借用了代码来创建这样一个管道:

passThruWInit :: Monad m => BS.ByteString -> C.Conduit BS.ByteString m BS.ByteString
passThruWInit initMsg = do
C.yield initMsg -- generate initial message first
C.awaitForever $ C.yield -- now pass-through conduit for all messages

现在,我们更新responseBody rsp $$+- sink代码以适应它介于两者之间:

responseBody rsp $=+ passThruWInit someInitMsg $$+- sink

最终结果是首先生成someInitMsg,然后流式传输responseBody内容。这样,我们可以将内容长度和其他元数据附加到可恢复的 HTTP 响应正文。

最新更新