Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iterate + forever = iterateM? Repeating an action with feedback

Tags:

haskell

monads

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.

like image 958
Asherah Avatar asked May 29 '12 14:05

Asherah


3 Answers

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?

like image 66
Daniel Wagner Avatar answered Nov 26 '22 16:11

Daniel Wagner


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))
like image 40
ertes Avatar answered Nov 26 '22 16:11

ertes


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
like image 35
mightybyte Avatar answered Nov 26 '22 15:11

mightybyte