簡體   English   中英

monadic動作的懶惰輸出

[英]Lazy output from monadic action

我有下一個monad變換器:

newtype Pdf' m a = Pdf' {
  unPdf' :: StateT St (Iteratee ByteString m) a
  }
type Pdf m = ErrorT String (Pdf' m)

基本上,它使用底層的Iteratee來讀取和處理pdf文檔(需要隨機訪問源,因此它不會一直將文檔保存在內存中)。

我需要實現一個保存pdf文檔的函數,我希望它是懶惰的,應該可以將文檔保存在常量內存中。

我可以生成懶惰的ByteString

import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as BS
save :: Monad m => Pdf m ByteString
save = do
  -- actually it is a loop
  str1 <- serializeTheFirstObject
  storeOffsetForTheFirstObject (BS.length str1)
  str2 <- serializeTheSecondObject
  storeOffsetForTheSecondObject (BS.length str2)
  ...
  strn <- serializeTheNthObject
  storeOffsetForTheNthObject (BS.length strn)
  table <- dumpRefTable
  return mconcat [str1, str2, ..., strn] `mappend` table

但實際輸出可能取決於以前的輸出。 (詳細信息:pdf文檔包含所謂的“引用表”,文檔中每個對象的絕對偏移量都以字節為單位。這絕對取決於ByteString pdf對象序列化的長度。)

如何確保save函數在將其返回給調用者之前不會強制執行整個ByteString

將回調作為參數更好並在每次輸出內容時調用它?

import Data.ByteString (ByteString)
save :: Monad m => (ByteString -> Pdf m ()) -> Pdf m ()

有更好的解決方案嗎?

要在一次傳遞中構建它,您將需要存儲(可能在狀態中)已寫入間接對象的位置。 所以保存需要跟蹤絕對字節位置,因為它工作 - 我沒有考慮你的Pdf monad是否適合這個任務。 當您到達最后時,您可以使用狀態中存儲的地址來創建外部參照部分。

我不認為雙通算法會有所幫助。

6月6日編輯:也許我現在更了解你的願望。 對於非常快速生成的文檔,例如HTML,有幾個hackage庫, 名稱中有“blaze”。 該技術是避免在ByteString上使用'mconcat'並在中間'構建器'類型上使用。 這個核心庫似乎是'blaze-builder' ,用於'blaze-html'和'blaze-textual'。

到目前為止我找到的解決方案是Coroutine示例:

proc :: Int -> Coroutine (Yield String) IO ()
proc 0 = return ()
proc i = do
  suspend $ Yield "Hello World\n" (proc $ i - 1)

main :: IO ()
main = do
  go (proc 10)
  where
  go cr = do
    r <- resume cr
    case r of
      Right () -> return ()
      Left (Yield str cont) -> do
        putStr str
        go cont

它執行與回調相同的工作,但調用者可以完全控制輸出生成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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