我正在尝试永远重复IO动作,但是将一次执行的结果输入到下一个。像这样:
-- poorly named
iterateM :: Monad m => (a -> m a) -> a -> m b
iterateM f a = f a >>= iterateM f
Hoogle似乎没有帮助我,但是我看到很多功能看起来非常接近我想要的功能,但似乎没有一个功能完全正确。
答案 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)
这实际上可以使用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