Arrow.hsを読もう〜その1〜

Arrow.hsを読もう、30分で読める範囲だけ。
ホントは30分でArrowを使って30分で何か作ろう、ってのがやりたいんだけど、まだプログラムが書けるほど理解していない。
しょうがないからコードリーディングをしよう。

方針

とりあえずArrowのインスタンスである->に関するコードだけを読む。Kleisli(..)とdo記法は無視。

関連してそうなやつ

モジュールからエクスポートされているやつ。

  • Arrow(..)
  • ArrowChoice(..)
  • ArrowApply(..)
  • ArrowMonad(..)
  • leftApp
  • ArrowLoop(..)
  • (>>>), (<<<)

(>>>), (<<<)

Arrowといえば(>>>)だけど、実はCategory.hsで定義されている。

-- | Right-to-left composition
(<<<) = (.)
f >>> g = g . f

(.)は関数合成の(.)じゃなくて、Categoryの(.)だけど、(->)なら2つは同じもの。

Arrow

(->)の実装。

instance Arrow (->) where
        arr f = f
        first f = f *** id
        second f = id *** f
--      (f *** g) ~(x,y) = (f x, g y)
--      sorry, although the above defn is fully H'98, nhc98 can't parse it.
        (***) f g ~(x,y) = (f x, g y)

arrはそのまま。

(***)がlazyパターンを使っている理由はよく分からないけど、関数2個とタプルを受けとって、それぞれに関数を適用するのか。firstとsecondは、それを楽に使えるようにしたやつだな。

Prelude Control.Arrow> (arr (+1) *** arr (+2)) (1,2)
(2,4)

あと各関数の型と、(&&&)のコード。

class Category a => Arrow a where
        arr :: (b -> c) -> a b c
        first :: a b c -> a (b,d) (c,d)
        second :: a b c -> a (d,b) (d,c)
        (***) :: a b c -> a b' c' -> a (b,b') (c,c')

        (&&&) :: a b c -> a b c' -> a b (c,c')
        f &&& g = arr (\b -> (b,b)) >>> f *** g

えっと、(>>>)がflip (.)と等価だから、bを受けとって(f b,g b)を変えすのか。

Prelude Control.Arrow> (arr (+1) &&& arr (+2)) 3
(4,5)

ArrowChoice

instance ArrowChoice (->) where
        left f = f +++ id
        right f = id +++ f
        f +++ g = (Left . f) ||| (Right . g)
        (|||) = either

(|||)が使ってるeither関数は

either :: (a -> c) -> (b -> c) -> Either a b -> c

という型らしい。はあはあ、Left用とRight用の関数をあらかじめ用意しておくのか。

Prelude Control.Arrow> (arr (+1) ||| arr (+2)) (Left 0)
1
Prelude Control.Arrow> (arr (+1) ||| arr (+2)) (Right 0)
2

(|||)がLeftとRightを合流させたから、(+++)はきっと分割させるんだろうな。Left xなら(Left (f x))に、Right yなら(Right (f y))になるのか。

Prelude Control.Arrow> (arr (+1) +++ arr (+2)) (Left 0)
Left 1
Prelude Control.Arrow> (arr (+1) +++ arr (+2)) (Right 0)
Right 2