我正在尝试将 Data.Binary.Put monad 包装到另一个 monad 中,以便以后可以询问它类似 "它要写入多少字节" 或者 "文件中的当前位置是什么" 的问题。但即使是非常简单的包装,比如:
data Writer1M a = Writer1M { write :: P.PutM a }
or
data Writer2M a = Writer2M { write :: (a, P.Put) }
我尝试过以下方法,但程序通常会崩溃(占用4GB的内存),并导致大量空间泄漏。
-- This works well and consumes almost no memory.
type Writer = P.Put
writer :: P.Put -> Writer
writer put = put
writeToFile :: String -> Writer -> IO ()
writeToFile path writer = BL.writeFile path (P.runPut writer)
-- This one will cause memory leak.
data Writer1M a = Writer1M { write :: P.PutM a }
instance Monad Writer1M where
return a = Writer1M $ return a
ma >>= f = Writer1M $ (write ma) >>= \a -> write $ f a
type WriterM = Writer1M
type Writer = WriterM ()
writer :: P.Put -> Writer
writer put = Writer1M $ put
writeToFile :: String -> Writer -> IO ()
writeToFile path writer = BL.writeFile path (P.runPut $ write writer)
-- This one will crash as well with exactly the
-- same memory foot print as Writer1M
data Writer2M a = Writer2M { write :: (a, P.Put) }
instance Monad Writer2M where
return a = Writer2M $ (a, return ())
ma >>= f = Writer2M $ (b, p >> p')
where (a,p) = write ma
(b,p') = write $ f a
type WriterM = Writer2M
type Writer = WriterM ()
writer :: P.Put -> Writer
writer put = Writer2M $ ((), put)
writeToFile :: String -> Writer -> IO ()
writeToFile path writer = BL.writeFile path (P.runPut $ snd $ write writer)
我是Haskell的新手,包装器Monad对我来说毫无意义,但似乎很简单,所以我猜肯定有一些显而易见的问题我没有注意到。
谢谢您的关注。
更新: 以下是一个示例代码,展示了这个问题:http://hpaste.org/43400/why_wrapping_the_databinaryp
更新2: 此问题还有第二部分,请参见此处。
newtype
替换“有问题”的包装器中的data
可以进一步减少差异。没有 -O2 标志会非常占用内存。你确定你重新编译时使用了 -O2 吗?试试-O2 -fforce-recomp
。 - Ed'ka