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