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