запись данных в основную монаду пут. И эта проблема существует в вашем исходном коде, который я изменил точно так же, как в коде, который я предоставил. Итак, время для нового подхода.

о моемупредыдущий вопросЯ пытаюсь заключить монаду Data.Binary.Put в другую монаду, чтобы позже я мог задать ей такие вопросы, как «сколько байт она собирается записать» или «какая текущая позиция в файле».

Раньше я думал, что понимание того, почему происходит утечка памяти при использовании тривиальной оболочки (IdentityT?), Приведет меня к решению моей проблемы. Но даже если вы, ребята, помогли мне решить проблему с тривиальной оболочкой, обертывание ее чем-нибудь полезным, например, StateT или WriterT, по-прежнему занимает слишком много памяти (и обычно вылетает).

Например, это один из способов, который я пытаюсь обернуть, и который приводит к утечке памяти для большого ввода:

type Out = StateT Integer P.PutM ()

writeToFile :: String -> Out -> IO ()
writeToFile path out = BL.writeFile path $ P.runPut $ do runStateT out 0
                                                         return ()

Вот более полный пример кода, демонстрирующий проблему

Что я хотел бы знать, это:

Что происходит внутри программы, что вызывает утечку памяти?Что я могу сделать, чтобы это исправить?

Что касается моего второго вопроса, я думаю, что я должен объяснить более подробно, что я собираюсь просматривать на диске: это в основном древовидная структура, где каждый узел дерева представлен в виде таблицы смещений для его дочерних элементов (плюс некоторые дополнительные данные). Таким образом, чтобы вычислить смещение n-х дочерних элементов в таблицу смещений, мне нужно знать размеры дочерних элементов от 0 до n-1 плюс текущее смещение (скажем, для упрощения, скажем, каждый узел имеет фиксированное количество дочерних элементов).

Спасибо за поиск.

ОБНОВЛЕНИЕ: благодаря nominolo я теперь могу создать монаду, которая оборачивается вокруг Data.Binary.Put, отслеживает текущее смещение и почти не использует память. Это делается путем отказа от использования преобразователя StateT в пользу другого механизма потоков состояния, который использует Continuations.

Нравится:

type Offset = Int

newtype MyPut a = MyPut
  { unS :: forall r . (Offset -> a -> P.PutM r) -> Offset -> P.PutM r }

instance Monad MyPut where
  return a = MyPut $ \f s -> f s a
  ma >>= f = MyPut $ \fb s -> unS ma (\s' a -> unS (f a) fb s') s

writeToFile :: String -> MyPut () -> IO ()
writeToFile path put =
  BL.writeFile path $ P.runPut $ peal put >> return ()
  where peal myput = unS myput (\o -> return) 0

getCurrentOffset :: MyPut Int
getCurrentOffset = MyPut $ \f o -> f o o

lift' n ma = MyPut $ \f s -> ma >>= f (s+n)

Однако у меня все еще есть проблема с отслеживанием количества байтов, которые MyPut собирается записать на диск. В частности, мне нужно иметь функцию с такой подписью:

getSize :: MyPut a -> MyPut Int
или же
getSize :: MyPut a -> Int

Мой подход заключался в том, чтобы обернуть монаду MyPut внутри преобразователя WriterT (что-то вродеэто). Но это снова стало занимать слишком много памяти. Как sclv упоминает в комментариях под nominolos ответом, WriterT каким-то образом нейтрализует эффект продолжений. Он также упоминает, что получение размера должно быть возможным непосредственно из монады MyPut, которая у меня уже есть, но все мои попытки сделать это закончились некомпилируемым кодом или бесконечным циклом: - |.

Может ли кто-нибудь помочь в дальнейшем?

Ответы на вопрос(2)

Ваш ответ на вопрос