Arrowのこと、その2

前回のポストに引き続き、Arrowを攻略していきたいと思います。

もう一度、Arrowのクラス定義を引っ張ってきます。

class Arrow a where
arr :: (b -> c) -> a b c
pure :: (b -> c) -> a b c
(>>>) :: a b c -> a c d -> a b d
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')

前回はarr(pure)と>>>を見てみました。残るメソッドのうち、Arrowのクラスインスタンスを定義するときに必ずインプリメントしなくてはいけないのはあとfirstだけです:

first :: (Arrow a) => a b c -> a (b, d) (c, d)

ここで、突然タプルが出てきます。おまけに、そのdって言うのはどっからでてきたんだ?という感じで、ちょっとびっくりなのですが、Arrowが関数を内包していること思い出すと、firstのやっていることは:

 a b c -> a (b, d) (c, d)
= (b -> c) -> ( (b, d) -> (c, d) )
= (b -> c) -> (b, d) -> (c, d)

ということで、要するに、タプルのfstに(b->c)を適用するということのようです。ということで、secondは当然のようにタプルのsndに(b->c)が適用されてることがわかりますね…

続いて(***)は、パラメタとしてArrowを2つとるコンビネータで、一つ目のArrowをタプルのfstに適用し、2つ目をsndにということで、
 (***) a1 a2 = first a1 >>> second a2
ということが言えそうです。そして、最後の(&&&)もArrowを2つとるコンビネータで、一つの入力を両方のArrowに適用して、その結果をタプルに入れて返してくれるArrow変換子のようです。

つまり、&&&は一つの入力を2つに分配し、first, second , ***は分配された後のデータに対する加工手段を提供しているということですね…ここまで見て、タプルはarrと>>>ではデータの流れが1本しか表現できなかったところに分岐と並列性を持ち込むために使われているようです...まぁ、関数で一つ以上の値が返したかったらタプルを使って返すのは常套手段ですから、そんなに奇抜なわけではないですね...クラス定義に突然出てくるので最初は面食らってしまいました。

と、ここまででArrow本体のクラスメソッドは終わりです。

ここまでくると、だいたいのパターンが見えてくるので、少しスピードが上がります。次に、ArrrowChoiceを見てみましょう。

class Arrow a => ArrowChoice a where
left :: a b c -> a (Either b d) (Either c d)
right :: a b c -> a (Either d b) (Either d c)
(+++) :: a b c -> a b' c' -> a (Either b b') (Either c c')
(|||) :: a b d -> a c d -> a (Either b c) d

といった形で、ここでは補助データ型としてEitherが使われています。EitherはなんとなくMaybeに似た型で:

data Either a b
= Left a
Right b
either :: (a -> c) -> (b -> c) -> Either a b -> c

ということで、Arrowの一本のデータの流れの中に2つの違うデータのどちらかという選択を可能にしてくれます…leftは入力データがLeftのときだけパラメタのArrowを適用する関数、Rightはその逆。(+++)はleftとrightを合成したもの。最後の(|||)はEither型の入力を1つにまとめるものですね...Choiceというだけあって、これは条件分岐に当たるものをArrowで表現するためにあるようですが、肝心の分岐の部分は提供されていません。Generalizing Monads to Arrowsの論文によると:

test:: (Arrow a) => a b Bool -> a b (Either b b)
test f = (f &&& arr id)>>> arr (\(p, b) -> if p then Left b else Right b)

を条件分岐に使えばif文のようなことができるとあります。なるほど...でも、これはライブラリの一部として提供されているわけではないんですね...ちょっと不思議...

最後はArrowLoop。

class Arrow a => ArrowLoop a where
loop :: a (b, d) (c, d) -> a b c

ふーむ、loopメソッドはfirstの逆のようなメソッドですね...何でこれがloopと呼ばれるのでしょう?普通のプログラミングで言うところのループとはちょっと違うものを話しているようです。CircularProgrammingって何?また、勉強してお話したいと思います。

今日はこの辺で...ではでは。