Reputation: 147
I am confused about defining foldM
in a do-block, mainly because of its recursion. The standard definition for foldM
is as follows:
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM _ a [] = return a
foldM f a (x:xs) = f a x >>= \fax -> foldM f fax xs
I understand this definition; the result of f a x
is passed into a new foldM
function recursively, until the list is empty. Here is a definition of foldM
in a do-block (copied from my uni course material):
foldM _ z [] = return z
foldM f z (x:xs) = do r <- foldM f z xs
f r x
I know that the definition of a do block is basically syntactic sugar for bind (>>=)
operations. However, I can't seem to grasp how this definition works. I find recursion in do-blocks overall very confusing. What is given to r
? How can the recursive line r <- foldM f z xs
be correct when foldM
is passed z
every recursive call? Shouldn't it be passed a recursively updated argument like f z x
, as in the foldM
definition with (>>=)
?
Upvotes: 3
Views: 968
Reputation: 105905
The latter variant evaluates the actions on the list from right-to-left, whereas the other one evaluates the actions on the list from right-to-left. Let's call the latter foldMDo
, so that we can keep them apart:
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM _ a [] = return a
foldM f a (x:xs) = f a x >>= \fax -> foldM f fax xs
foldMDo _ z [] = return z
foldMDo f z (x:xs) = do r <- foldMDo f z xs
f r x
If we use them to print a list, the difference is immediately obvious:
ghci> foldM (\_ y -> print y) () [1..10]
1
2
3
4
5
6
7
8
9
10
ghci> foldMDo (\_ y -> print y) () [1..10]
10
9
8
7
6
5
4
3
2
1
So, let's desugar the do
variant:
do r <- foldMDo f z xs
f r x
is the same as
foldMDo f z xs >>= \fzxs -> f fzxs x
Let's compare this to the other one next to each other:
(1) f a x >>= \fax -> foldM f fax xs
(2) foldMDo f z xs >>= \fzxs -> f fzxs x
We can compare that to foldl
and foldr
with explicit let...in
:
foldr f a (x:xs) = let acc = foldr f a xs
in f a acc
foldl f a (x:xs) = let acc = f a x
in foldr f acc xs
The uni material looks like foldr
, whereas the default one looks like foldl
, as it is expected with the order of evaluation.
And for completion, let's add the do
version of your foldM
:
foldM f a (x:xs) = do fax <- f a x
foldM f fax xs
Your uni course material doesn't implement foldM
, since it doesn't do left-to-right evaluation, but instead right-to-left. Essentially,
Upvotes: 2