9

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.

Jeff Burdges
  • 4,204
  • 23
  • 46

1 Answers1

11

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 seqing 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.

ehird
  • 40,602
  • 3
  • 180
  • 182
  • Ahh, I suppose the monad could make things strict if it had strict flags in it, but not otherwise. And no standard monads do so. Thanks! – Jeff Burdges Jan 18 '12 at 23:39
  • 3
    @JeffBurdges - here's sort of an ad-hoc observation of which Monadic `>>=` are strict vs lazy: http://stackoverflow.com/a/8250334/208257 – Dan Burton Jan 19 '12 at 01:09
  • Indeed, I doubt `foldM'` would help if your `(>>=)` is too lazy, since e.g. forcing a value returned by the lazy `State` monad doesn't necessarily force the state. – ehird Jan 19 '12 at 01:11