我想永远重复一个IO动作,它们只是一个执行到下的结果。 事情是这样的:
-- poorly named
iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f a = f a >>= iterateM f
Hoogle似乎并没有帮助我,但我看到很多看起来诱人接近我想要的功能,但没有人可以走到一起,是它到底。
我想永远重复一个IO动作,它们只是一个执行到下的结果。 事情是这样的:
-- poorly named
iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f a = f a >>= iterateM f
Hoogle似乎并没有帮助我,但我看到很多看起来诱人接近我想要的功能,但没有人可以走到一起,是它到底。
你说得对,我不知道这个特别的循环方式实现的地方。 你的实现看起来不错; 为什么不提交它作为一个补丁的单子,循环包装?
好吧,我会期待一个iterateM
组合子有这种类型的签名:
iterateM :: (Monad m) => (a -> m a) -> a -> m [a]
当然,这并不是一个非常有用的组合子,因为你不能提取结果在大多数单子。 一个更明智的名义去与你的组合子将是基地命名标准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))
我相信你不会看到这个标准库的原因是因为它永远不会终止。 在迭代函数可以利用惰性列表,让您使用指定终止take
结果列表功能。 在这里,你的结果是一元,所以这是不可能的。
显然,你的精神理念可以做到的。 它只是看起来有点不同:
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
这其实可以写成方面forever
使用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