iterate + forever = iterateM? Repeating an action with feedback

时间秒杀一切 提交于 2019-12-10 13:02:49

问题


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.


回答1:


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?




回答2:


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))



回答3:


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



回答4:


This can actually be written in terms of forever using StateT.

import Control.Monad.Trans.State
import Control.Monad.Trans.Class (lift)
import Control.Monad (forever)

iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f = evalStateT $ forever $ get >>= lift . f >>= put


来源:https://stackoverflow.com/questions/10801036/iterate-forever-iteratem-repeating-an-action-with-feedback

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!