У меня есть следующая монада трансформатор:Ленивый выходе из монадических действий
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()
Есть ли лучшее решение?
Я просто добавил «реализацию» для функции 'save', чтобы устранить проблему. Да, это должен быть 1-проходный алгоритм, но это не проблема. Сама проблема: когда я вызываю 'mconcat' для создания окончательной ленивой' ByteString', у меня уже есть ее в памяти. Предполагая, что очень большой файл PDF, у меня недостаточно памяти. Я хочу хранить только смещения, а не 'ByteString'. Похоже, подход обратного вызова решает проблему, но я думаю, что лучшее решение должно существовать. – Yuras
Странно, но я не получил уведомление о вашем редактировании в 6 июня. Как «blaze-builder» может мне помочь? «Bulder» определенно быстрее, чем 'ByteString', когда вы хотите« mappend », но проблема в использовании памяти, а не в производительности. 'str1',' str2' и т. д. будут уже в памяти (принудительно BS.length) до 'mconcat'. – Yuras