iterate + forever = iterateM?通过反馈重复操作

时间:2012-05-29 14:19:54

标签: haskell monads

我正在尝试永远重复IO动作,但是将一次执行的结果输入到下一个。像这样:

-- poorly named
iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f a = f a >>= iterateM f

Hoogle似乎没有帮助我,但是我看到很多功能看起来非常接近我想要的功能,但似乎没有一个功能完全正确。

4 个答案:

答案 0 :(得分:4)

好吧,我希望iterateM组合器有这种类型的签名:

iterateM :: (Monad m) => (a -> m a) -> a -> m [a]

当然这不是一个非常有用的组合器,因为你无法在大多数monad中提取结果。使用组合器的基本命名标准的更合理的名称是iterateM_

iterateM_ :: (Monad m) => (a -> m a) -> a -> m b
iterateM_ f = fix $ \again x -> f x >>= again

这个组合器很有用:

countFrom :: (Enum a) => a -> IO b
countFrom = iterateM_ (\x -> succ x <$ print x)

但是,为了简单起见,我将使用fix或显式递归。显式递归代码不会更长或更不易读取:

countFrom :: (Enum a) => a -> IO b
countFrom = fix (\again x -> print x >> again (succ x))

答案 1 :(得分:4)

你是对的,我不知道这个特定类型的循环实现的地方。你的实现看起来很好;为什么不将它作为补丁提交给monad-loops包?

答案 2 :(得分:3)

我相信你在标准库中没有看到这个的原因是因为它永远不会终止。迭代函数可以利用惰性列表来允许您使用结果列表上的take函数指定终止。在这里,你的结果是monadic,所以这是不可能的。

显然,你的想法的精神可以做到。它只需看起来有点不同:

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

答案 3 :(得分:1)

这实际上可以使用foreverStateT来表示。

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