将一个 ResumableSource 附加到另一个
Appending one ResumableSource to another
当一个 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
我问它的原因是因为我有一个 HTTP ResumableSource
与上面的 b
类型相同,我想在将其送入接收器之前在其前面添加内容长度。目前,它看起来像这样:
responseBody rsp $$+- sink
我想改成这样:
((newResumableSource (yield content-len)) >> (responseBody rsp)) $$+- sink
将初始消息添加到 ResumableSource
之前的一个好方法似乎是使用 conduit
生成初始消息然后成为传递。在这里,我借用了 map
conduit 的代码来创建这样一个 conduit:
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 响应正文中。
当一个 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
我问它的原因是因为我有一个 HTTP ResumableSource
与上面的 b
类型相同,我想在将其送入接收器之前在其前面添加内容长度。目前,它看起来像这样:
responseBody rsp $$+- sink
我想改成这样:
((newResumableSource (yield content-len)) >> (responseBody rsp)) $$+- sink
将初始消息添加到 ResumableSource
之前的一个好方法似乎是使用 conduit
生成初始消息然后成为传递。在这里,我借用了 map
conduit 的代码来创建这样一个 conduit:
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 响应正文中。