Data.Traversable.for

おもしろそうだったので、Traversable.forについて少し調べました。

Data.Traversable.forはモナドに対してはmapMと同じ挙動をします。

for [1..10] print

mapM print [1..10]

は同じです。

例えばリストモナドだとこういう事ができます。

> for [1, 2, 3, 4] (\i -> [i, -i])
[[1,2,3,4],[1,2,3,-4],[1,2,-3,4],[1,2,-3,-4],[1,-2,3,4],[1,-2,3,-4],[1,-2,-3,4],[1,-2,-3,-4],
[-1,2,3,4],[-1,2,3,-4],[-1,2,-3,4],[-1,2,-3,-4],[-1,-2,3,4],[-1,-2,3,-4],[-1,-2,-3,4],[-1,-2,-3,-4]]

Traversable.forがmapMと違う点の1つめは、リストに対してでなくてもトラバースできるという点です。
Maybeの場合だと

> for (Just "hello") putStrLn
hello
> for Nothing putStrLn
(何も表示されない)

という挙動になります。例えばリストと組み合わせて、Justの時だけ処理をするとかいった書き方ができます。

> for [Just "hello", Nothing, Just " ", Nothing, Just "world"] (\i -> for i putStr)
hello world

他にはMapとかTreeとかなどにforが使えます。

mapMと異なる点の2つめは、第2引数はモナドを返す関数(a -> m b型)で無くてもいいということです。第2引数はApplicativeを返す関数(a -> f b型)なら何でもいいです。

標準ライブラリに定義されているApplicativeのインスタンス
* IO
* []
* Maybe
* (->) a
* Monoid a => (,) a
(以下はApplicative内で新しく用意されているもの)
* Monoid m => Const m
* Monad m => WrappedMonad m
* Arrow a => WrappedArrow a b
* ZipList a
などです。

Maybeまでは結構直感的に分かるんですが、それ以降は不可思議な挙動をします。
例えば(->) aと(,) aについてですが、以下のような挙動をします。

> for [1, 2, 3, 4] (+) 1
[2,3,4,5]
> for [1, 2, 3, 4] (\i -> ((), i+1))
((),[2,3,4,5])

なぜこうなるかの説明するためはApplicative, Functor, Monoidの説明を書かないといけないので、ちょっとできません。
とりあえずは、これらが役に立つ様な場面があるのかどうかを先に考えてみます。