当一个ResumableSource
不是明确的Monad
实例时,如何将它们附加到另一个?下面是一个玩具示例 -a
有Monad
约束,而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 响应正文。