したがって、それを理解する最良の方法は、それを行うことです。以下にの代わりにをfoldlM
使用する実装がfoldl
ありfoldr
ます。それは良い練習問題です。試してみて、後で私が提案する解決策に進んでください。この例は、私がそれを達成するために行ったすべての推論を説明しています。これは、あなたの推論とは異なる場合があり、関数アキュムレータの使用についてすでに知っているため、バイアスになる可能性があります。
ステップ1:のfoldlM
観点から書いてみましょうfoldl
-- this doesn't compile because f returning type is (m b) and not just (b)
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f z0 xs
-- So let substitute f by some undefined f'
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' = undefined
-- cool, but f' should use f somehow in order to get the monadic behaviour
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' b a = f somethingIDontkNow
ここでf'
は、それが純粋でありf
、型の一致の結果を抽出する必要があることに気付きます。モナディック値を「抽出」する唯一の方法は演算子を使用することですが>>=
、そのような演算子は使用した直後にラップする必要があります。
結論として:あなたが私と一緒に終わるたびに、このモナドを完全にアンラップしたいときは、あきらめてください。正しい方法ではありません
ステップ2:パターンマッチングが簡単なので(つまり、実際にを使用する必要がないため)、まず折りたたみ可能として使用して書いfoldlM
てみましょう。foldl
[]
fold
-- This is not very hard. It is pretty standard recursion schema. :)
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
それは簡単でした。foldl
定義をリストの通常の定義と比較してみましょう
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z0 [] = z0
myfoldl f z0 (x:xs) = foldl f (f z0 x) xs
涼しい!!それらはほとんど同じです。取るに足らないケースは、まったく同じものです。再帰的なケースは少し異なります。次のようなものを書きたいと思いますfoldlM' f (f z0 x) xs
。しかし、ステップ1のようにコンパイルされないので、大丈夫f
>>=
だと思うかもしれませんが、私はを適用したくありません。そのような計算を保持して、でそれを作成するだけです。foldlM' f (f z0 x >>=) xs
理にかなっているようなものを書きたい ...
ステップ3累積したいのは関数の合成であり、結果ではないことを認識してください。(ここでは、あなたが投稿したので私がすでに知っているという事実におそらく偏っています)。
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' initFunc xs
where initFunc = undefined :: b -> m b
f' = undefined :: (b -> m b) -> a -> (b -> m b) -- This type signature can be deduce because f' should be applied to initFunc and a's from t a.
initFunc
ステップ2(再帰的な定義)からの知識のタイプと使用によって、それを推測できinitFunc = return
ます。およびを使用する必要があることをf'
知ってf'
いるf
と、の定義を完了することができます>>=
。
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' return xs z0
-- ^^^^^^
-- |- Initial value
where f' b a = \bvalue -> b bvalue >>= \bresult -> f bresult a -- this is equivalent to (b >=> \result -> f result a) which captures the sequence behaviour of the implementation
-- ^ ^^^^^^ ^^^^^^^
-- | | |- This is the result of previous computation
-- | |- f' should return a function b -> m b. Any time you have to return a function, start writing a lambda
-- |- This b is the accumulated value and has type b -> m b
-- Following the types you can write this with enough practise
ご覧のとおり、それを行うことはそれほど難しくありません。それは練習が必要ですが、私はプロのHaskell開発者ではなく、自分でそれを行うことができました。それは練習の問題です