问题
How does one fold over a monad strictly? Data.Foldable has the strict foldl'
and the monadic foldlM
, but no strict foldlM'
? Is the strictness somehow defined by the monad itself? If so, how does one work out what it is?
Imagine I must determine whether the product of an enormous list of ring elements is zero, but my ring isn't an integral domain, i.e. it contains zero devisors. In this case, I should tail recursively foldl
my multiplication ***
over the list, but return False
the moment the product becomes zero, rather than waiting on the full product.
safelist :: [p] -> Bool
safelist [] = True
safelist (x:xs) = snd $ foldl' f (x,True) xs
where f (u,b) v = (w, b && w /= Zero) where w = u *** v
I could perhaps simplify this code slightly using the Maybe
monad's foldlM
but doing so seemingly lacks the required strictness.
回答1:
There's no such standard function, but it's easy to define:
foldM' :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM' _ z [] = return z
foldM' f z (x:xs) = do
z' <- f z x
z' `seq` foldM' f z' xs
This is just the standard foldM
, but with the same seq
ing in it that foldl'
does (compared to foldl
). It's probably not defined anywhere standard because it's not likely to be all that useful: for most monads, (>>=)
is "strict" in the sense you need to use a left-fold without overflowing the stack; this is only useful when your excessive thunks are in the returned values themselves, but a useful application of foldM
will perform some monadic computation with the value from the last step, making that unlikely.
I think your code is simple enough as it is; I doubt foldM'
would make it any more elegant.
来源:https://stackoverflow.com/questions/8919026/does-haskell-have-foldlm