幅広い入力をフォールディングする汎用関数を作成したかった (リスト、バイト文字列、テキスト (およびおそらく他の同様の表現) で単一の関数を機能させるを参照)。1つの答えが示唆したように、ListLikeはまさにそのためのものです。そのFoldableLLクラスは、折り畳み可能なあらゆるものの抽象化を定義します。ただし、モナドフォールドが必要です。したがって、 /foldM
の観点から定義する必要があります。foldl
foldr
これまでのところ、私の試みは失敗しました。定義してみた
foldM'' :: (Monad m, LL.FoldableLL full a) => (b -> a -> m b) -> b -> full -> m b
foldM'' f z = LL.foldl (\acc x -> acc >>= (`f` x)) (return z)
しかし、大規模な入力ではメモリが不足します。未評価の大きな計算ツリーが構築されます。たとえば、大きなテキスト ファイルを
main :: IO ()
main = getContents >>= foldM'' idx 0 >> return ()
where
-- print the current index if 'a' is found
idx !i 'a' = print i >> return (i + 1)
idx !i _ = return (i + 1)
すべてのメモリを消費して失敗します。
問題は、モナド計算が間違った順序で構成されていることだと感じています-((... >>= ...) >>= ...)
代わりに、(... >>= (... >>= ...))
しかしこれまでのところ、それを修正する方法がわかりませんでした。
回避策:ListLike
が公開されているので、アキュムレータを状態モナドにラップして smapM_
を構築foldM
しました。ListLike
modifyT :: (Monad m) => (s -> m s) -> StateT s m ()
modifyT f = get >>= \x -> lift (f x) >>= put
foldLLM :: (LL.ListLike full a, Monad m) => (b -> a -> m b) -> b -> full -> m b
foldLLM f z c = execStateT (LL.mapM_ (\x -> modifyT (\b -> f b x)) c) z
これは大規模なデータ セットでは問題なく機能しますが、あまり適切ではありません。そして、元の質問には答えません。それを単にFoldableLL
(なしでmapM_
) データで定義できる場合です。