I'm trying to repeat an IO action forever, but feeding the result of one execution into the next. Something like this:
-- poorly named
iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f a = f a >>= iterateM f
Hoogle didn't seem to help me, but I see plenty of functions which look enticingly close to what I want, but none seem to come together to be exactly it.
You're right, I don't know of a place this particular kind of loop is implemented. Your implementation looks fine; why not submit it as a patch to the monad-loops package?
Well, I would expect an iterateM
combinator to have this type signature:
iterateM :: (Monad m) => (a -> m a) -> a -> m [a]
Of course this is not a very useful combinator, because you couldn't extract the result in most monads. A more sensible name to go with the base naming standard for your combinator would be iterateM_
:
iterateM_ :: (Monad m) => (a -> m a) -> a -> m b
iterateM_ f = fix $ \again x -> f x >>= again
This combinator can be useful:
countFrom :: (Enum a) => a -> IO b
countFrom = iterateM_ (\x -> succ x <$ print x)
However, for the sake of simplicity I would just go with fix
or explicit recursion. The explicitly recursive code isn't much longer or much less readable:
countFrom :: (Enum a) => a -> IO b
countFrom = fix (\again x -> print x >> again (succ x))
I believe the reason you don't see this in the standard libraries is because it will never terminate. The iterate function can leverage lazy lists to allow you to specify termination using the take
function on the result list. Here, your result is monadic, so this isn't possible.
Obviously the spirit of your idea can be done. It just has to look a little different:
iterateM :: Monad m => Int -> (a -> m a) -> a -> m a
iterateM 0 _ a = return a
iterateM n f a = f a >>= iterateM (n-1) f
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With