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の説明を書かないといけないので、ちょっとできません。
とりあえずは、これらが役に立つ様な場面があるのかどうかを先に考えてみます。