[英]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.