mcm*_*yer 1 monads haskell lazy-evaluation monad-transformers
已经讨论过,mapM这里本来就不是懒惰的,例如这里和这里.现在我正在努力解决这个问题的变化,其中mapM问题是在monad变换器堆栈内部.
这是一个函数,它取自我在gist.github.com上使用的LevelDB的具体,工作(但是空间泄漏)的例子:
-- read keys [1..n] from db at DirName and check that the values are correct
doRead :: FilePath -> Int -> IO ()
doRead dirName n = do
success <- runResourceT $ do
db <- open dirName defaultOptions{ cacheSize= 2048 }
let check' = check db def in -- is an Int -> ResourceT IO Bool
and <$> mapM check' [1..n] -- space leak !!!
putStrLn $ if success then "OK" else "Fail"
Run Code Online (Sandbox Code Playgroud)
此函数读取与键对应的值[1..n]并检查它们是否正确.ResourceT IO amonad 里面的麻烦线是
and <$> mapM check' [1..n]
Run Code Online (Sandbox Code Playgroud)
一个解决办法是使用流媒体库,比如pipes,conduit等但这些似乎比较重,我根本不知道如何在这种情况下使用它们.
我调查的另一条路径ListT就像这里建议的那样.但是ListT.fromFoldable :: [Bool]->ListT Bool和ListT.fold :: (r -> a -> m r) -> r -> t m a -> mr(where m= IOand a,r= Bool)的类型签名与手头的问题不匹配.
什么是摆脱空间泄漏的"好方法"?
更新:请注意,此问题与monad变换器堆栈无关!以下是建议解决方案的摘要:
1)使用Streaming:
import Streaming
import qualified Streaming.Prelude as S
S.all_ id (S.mapM check' (S.each [1..n]))
Run Code Online (Sandbox Code Playgroud)
2)使用Control.Monad.foldM:
foldM (\a i-> do {b<-check' i; return $! a && b}) True [1..n]
Run Code Online (Sandbox Code Playgroud)
3)使用 Control.Monad.Loops.allM
allM check' [1..n]
Run Code Online (Sandbox Code Playgroud)
我知道你提到你不想使用流媒体库,但你的问题似乎很容易通过流媒体解决而不会过多地改变代码.
import Streaming
import qualified Streaming.Prelude as S
Run Code Online (Sandbox Code Playgroud)
我们使用each [1..n]而不是[1..n]获取元素流:
each :: (Monad m, Foldable f) => f a -> Stream (Of a) m ()
Run Code Online (Sandbox Code Playgroud)
流式传输纯净的可折叠容器的元素.
(我们也可以写一些类似的东西S.take n $ S.enumFrom 1).
我们用S.mapM check'而不是mapM check':
mapM :: Monad m => (a -> m b) -> Stream (Of a) m r -> Stream (Of b) m r
Run Code Online (Sandbox Code Playgroud)
使用monadic操作的结果替换流的每个元素
然后我们用以下方法折叠布尔流S.all_ id:
all_ :: Monad m => (a -> Bool) -> Stream (Of a) m r -> m Bool
Run Code Online (Sandbox Code Playgroud)
把它们放在一起:
S.all_ id (S.mapM check' (S.each [1..n]))
Run Code Online (Sandbox Code Playgroud)
与您开始使用的代码没有太大区别,也不需要任何新的运算符.