簡體   English   中英

在流式傳輸管道中將 ResourceT 與支架組合

[英]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 ()

問題

  1. 如何使此代碼進行類型檢查並仍然確保其安全地釋放calculateA function 中的資源?
  2. 我正在使用C.readFile讀取多個文件,然后將其包裝在runResourceT中。 這會正確釋放所有文件句柄嗎?
  3. 構圖好嗎? (請注意,我需要calculateA function 與myLinesStream分開)

問題是您正在嘗試將bracket與過於籠統的 monad 一起使用。 bracket有簽名:

bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c   

它需要IO動作作為參數。 但是,您傳遞給bracketgo function 需要在calculateA的調用者選擇的通用基礎 monad m中工作(您稍后將該 monad 設為main中的ResourceT IO )。

來自baseResourceTbracket不能很好地混合。 相反,您需要轉向 resourcet package 中的特殊函數,例如allocaterelease ,並使用它們來定義一個 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM