Haskell は関数型プログラミング言語のため、他のプログラミング言語における「for
ループ」そのものはありません。
ただし、モナドを返す場合はそれに似た書き方が可能です。
1. 再帰
Haskell ではモナドかどうかにかかわらず、再帰でループすることができます。
本記事では深く触れませんが、fix
関数を用いると無名関数で再帰することができます。
import Control.Monad (when)
import Data.Function (fix)
f :: Int -> IO ()
f x = do
when (x > 1) $ do
f $ x - 1
putStrLn $ "f: " ++ show x
main :: IO ()
main = do
f 3
fix
(\ (f' :: Int -> IO ()) (x :: Int) -> do
when (x > 1) $ do
f' $ x - 1
putStrLn $ "fix: " ++ show x
)
3
flip fix 3 $ \ (f' :: Int -> IO ()) (x :: Int) -> do
when (x > 1) $ do
f' $ x - 1
putStrLn $ "flip fix: " ++ show x
f: 1
f: 2
f: 3
fix: 1
fix: 2
fix: 3
flip fix: 1
flip fix: 2
flip fix: 3
2. モナドをループさせる関数
モナドをループさせる関数として以下のようなものがあります:
-
traverse_ f xs
,for_ xs f
-
xs
の各要素x
に関数f
を適用する
-
-
replicateM_ n x
-
x
をn
回繰り返す
-
-
foldM_ f a xs
-
xs
の各要素x
に関数f
を適用する - 「関数
f
の戻り値pure a'
」のa'
を次のループ時の「関数f
の引数a
」とする
-
-
zipWithM_ f xs ys
-
xs
の各要素x
とys
の各要素y
を対にして関数f
を適用する
-
-
sequenceA_ xs
-
xs
の各要素x
を実行する
-
※ for_ = flip traverse_
です。
※他に歴史的な理由により残されている関数がありますが、mapM_ = traverse_
, forM_ = for_
および sequence_ = sequenceA_
です。
※各関数の型の説明は後述。
参考「traverse_ - Data.Foldable」
参考「for_ - Data.Foldable」
参考「replicateM_ - Control.Monad」
参考「foldM_ - Control.Monad」
参考「zipWithM_ - Control.Monad」
参考「sequenceA_ - Data.Foldable」
2.1. 使い方
IO ()
型に関して各関数は以下のように使用します。
import Control.Monad (foldM_, replicateM_, zipWithM_)
import Data.Foldable (for_, sequenceA_, traverse_)
main :: IO ()
main = do
traverse_
(\ (x :: Int) -> do
putStrLn $ "traverse_: " ++ show x
)
[1..3]
for_ [1..3] $ \ (x :: Int) -> do
putStrLn $ "for_: " ++ show x
replicateM_ 3 $ do
putStrLn "replicateM_"
foldM_
(\ (a :: Int) (x :: Int) -> do
putStrLn $ "foldM_: " ++ show x ++ ", " ++ show a
pure $ a + 1
)
4
[1..3]
zipWithM_
(\ (x :: Int) (y :: Int) -> do
putStrLn $ "zipWithM_: " ++ show x ++ ", " ++ show y
)
[1..3]
[4..6]
sequenceA_
[ putStrLn "sequenceA_: 1"
, putStrLn "sequenceA_: 2"
, putStrLn "sequenceA_: 3"
]
traverse_: 1
traverse_: 2
traverse_: 3
for_: 1
for_: 2
for_: 3
replicateM_
replicateM_
replicateM_
foldM_: 1, 4
foldM_: 2, 5
foldM_: 3, 6
zipWithM_: 1, 4
zipWithM_: 2, 5
zipWithM_: 3, 6
sequenceA_: 1
sequenceA_: 2
sequenceA_: 3
2.2. 型
各関数の型は以下のようになっています。
traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
for_ :: (Foldable t, Applicative f) => t a -> (a -> f b) -> f ()
replicateM_ :: Applicative m => Int -> m a -> m ()
foldM_ :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m ()
zipWithM_ :: Applicative m => (a -> b -> m c) -> [a] -> [b] -> m ()
sequenceA_ :: (Foldable t, Applicative f) => t (f a) -> f ()
ここで t = []
, f = IO
および m = IO
とすると、以下のようになります。
traverse_ :: (a -> IO b) -> [a] -> IO ()
for_ :: [a] -> (a -> IO b) -> IO ()
replicateM_ :: Int -> IO a -> IO ()
foldM_ :: (b -> a -> IO b) -> b -> [a] -> IO ()
zipWithM_ :: (a -> b -> IO c) -> [a] -> [b] -> IO ()
sequenceA_ :: [IO a] -> IO ()
戻り値が不要なところを IO ()
型とすると、以下のようになります。
traverse_ :: (a -> IO ()) -> [a] -> IO ()
for_ :: [a] -> (a -> IO ()) -> IO ()
replicateM_ :: Int -> IO () -> IO ()
foldM_ :: (b -> a -> IO b ) -> b -> [a] -> IO ()
zipWithM_ :: (a -> b -> IO ()) -> [a] -> [b] -> IO ()
sequenceA_ :: [IO ()] -> IO ()