[英]Combining ResourceT with bracket in a streaming pipeline
這是我的代碼的簡化:
import Database.PostgreSQL.Simple (Connection)
import qualified Streaming.Prelude as S
import Streaming.ByteString.Char8 as C
import Streaming.Zip (gunzip)
import Streaming
main :: IO ()
main = do
res <- runResourceT $ calculateA myLinesStream
return ()
type MyLinesStream m r = S.Stream (S.Of String) m r
connect :: IO Connection
connect = undefined
close :: Connection -> IO ()
close = undefined
calculateA :: MonadIO m => MyLinesStream m r -> m ()
calculateA stream = liftIO (bracket connect close (go stream))
where
go :: MonadIO m => MyLinesStream m r -> Connection -> m ()
go stream conn = stream & S.length_ >>= liftIO . print
myLinesStream :: (MonadIO m, MonadResource m) => MyLinesStream m ()
myLinesStream = do
S.each ["1.zip", "2.zip"]
& S.mapM (\fileName -> C.readFile fileName & gunzip)
& S.mconcat
& C.lines
& mapsM (S.toList . C.unpack)
& void
go stream
上的以下行存在類型錯誤:
calculateA stream = liftIO (bracket connect close (go stream))
錯誤說:
Couldn't match type ‘m’ with ‘IO’
‘m’ is a rigid type variable bound by
the type signature for:
calculateA :: forall (m :: * -> *) r.
MonadIO m =>
MyLinesStream m r -> m ()
Expected type: Connection -> IO ()
Actual type: Connection -> m ()
calculateA
function 中的資源?C.readFile
讀取多個文件,然后將其包裝在runResourceT
中。 這會正確釋放所有文件句柄嗎?calculateA
function 與myLinesStream
分開)問題是您正在嘗試將bracket
與過於籠統的 monad 一起使用。 bracket
有簽名:
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
它需要IO
動作作為參數。 但是,您傳遞給bracket
的go
function 需要在calculateA
的調用者選擇的通用基礎 monad m
中工作(您稍后將該 monad 設為main
中的ResourceT IO
)。
來自base和ResourceT
的bracket
不能很好地混合。 相反,您需要轉向 resourcet package 中的特殊函數,例如allocate
和release
,並使用它們來定義一個 helper,例如:
bracketStream :: (Functor f, MonadResource m)
=> IO a
-> (a -> IO ())
-> (a -> Stream f m b)
-> Stream f m b
bracketStream alloc free inside = do
(key, seed) <- lift (allocate alloc free)
r <- inside seed
release key
pure r
它是如何工作的? 如果你有一個X
s 的 stream,它會在 stream 的開頭添加一個分配操作(注冊在異常終止的情況下調用相應的清理操作,如異常)並且它還會在以下情況下添加對清理操作的顯式調用stream 用完了:
(分配+注冊清理) XX X... X (清理)
你寫了:
我正在使用 C.readFile 讀取多個文件,然后將其包裝在 runResourceT 中。 這會正確釋放所有文件句柄嗎?
是的。 使用ResourceT
時,資源將在執行顯式清理操作時或在我們使用runResourceT
“退出” ResourceT
時(可能異常,有異常)釋放。
因此,如果我們讀取 X 的 stream 后跟 Y 的 stream,我們將得到:
(allocate+register cleanup) X X X ... X (cleanup) (allocate+register cleanup) Y Y Y ... Y (cleanup)
也就是說,在分配產生 Y 的資源之前,會先釋放產生 X 的資源。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.