do
式の中でループを書く場合,イチイチ let
を使うのは面倒だなぁ,
と常々考えていたのですが,よく考えたら これ, fix
を使えば済む話ですね:
import Data.IORef import Data.Function main = do -- 無引数無名再帰 a_ref <- newIORef ( 0 :: Int ) fix $ \loop -> do a <- readIORef a_ref if a < 10 then do print a writeIORef a_ref (a+1) loop else return () -- 引数付き無名再帰 sum <- newIORef ( 0 :: Int ) ( `fix` 0 ) $ \ loop i -> do if i < 10 then do modifyIORef sum(+ i) loop $ i + 1 else return () print =<< readIORef sum
IORef
と組み合わせれば かなり手続き的に書けて,個人的に大満足です.
まぁ,継続モナドを使う方が楽なケースも多いですが.
あ,継続モナドに関しての詳しい解説は, Haskell Advent Calendar 2011 の僕の番の時にでも.
追記
当たり前ですが,今回のような例では
main = do forM_ [0..9] $ \i -> do print i print $ sum [0..9]
と書けば それで済む話ですし,そう書いた方がエレガントです.
ただ,実際のプログラミングでは,そうではないケースも多いわけで,
そういう場合に,いちいち名前のある再帰関数を定義したり
継続モナドで forever
を使ったりするのは,何か違うんじゃね? と.
そもそも Haskell の Control.Monad には,手続き言語で言う while
系列の
「途中で処理を中止する」関数が存在しないんですよね.
仕方ないので
takeWhileM_ :: Monad m => ( a -> m Bool ) -> [a] -> m () takeWhileM_ p = ( `foldr` return () ) $ \x action -> do cond <- p x if cond then action else return () main = do (`takeWhileM_` [1..] ) $ \i -> do if i < 10 then do print i return True else return False
的な関数を用意してみても,どうも読みやすいようには書けない(( return True
とかが邪魔になるので)).
仕方ないので継続モナドを使うことも考えましたが,それはそれで liftIO
がキモい.
どうしたもんかな,と思って,色々と試行錯誤した結果,
(\f -> foldr f (return ()) [1..] ) $ \_ action -> do いろいろと束縛 if 条件 then do 処理 action else return ()
的なコードに至り,その後,あれ,これって無名再帰と似てね? と思い至った次第.