Applicative勉強中:Applicativeパターン

さて、昨日のtransposeなのですが、

transpose :: [ [a ] ] -> [ [a ] ]
transpose [ ] = repeat [ ]
transpose (xs : xss) = zipWith (:) xs (transpose xss)

これは、つまりtranspose [ [1, 2, 3], [4, 5, 6] ]を

[1, 2, 3]
`zipWith (:)`
[4, 5, 6]
`zipWith (:)`
[ [ ], [ ], [ ] ]

のように実現しているわけですが、これをこうゆう風に実現することもできます。

[ 1 , 2 , 3 ]
[ : , : , : ]
[ 4 , 5 , 6 ]
[ : , : , : ]
[ [ ], [ ], [ ] ]

こうすることで、処理の終端(空リストのリストの生成のところ)だけでなく、関数リストの生成にもrepeatが使えるようになりそうです。
ただし、ただリストを並べて書いても勝手にくっついてはくれないので、リスト同士を掛け合わせるコンビネータ(糊)がひつようですね。でもその前に、さらにひねって:

[ : , : , : ]
[ 1 , 2 , 3 ]
[ : , : , : ]
[ 4 , 5 , 6 ]
[ [ ], [ ], [ ] ]

はどうでしょう?最初の2行をくっつけた結果がこうなってほしい:

[1:, 2:, 3:]

そうすれば、

[ : , : , : ]
[ 1 , 2 , 3 ]
[ 4:, 5:, 6: ]
[ [ ], [ ], [ ] ]
 ||
[ 1:, 2:, 3: ]
[ [4],[5],[6] ]
 ||
[ [1, 4], [2, 5], [3, 6] ]

と、ばっちりですね。
だとすれば、ここで必要になる「糊」は次のようなものです...zipの代わりのapplicativeという意味でzappという関数を名前がついています。

zapp :: [a -> b] -> [a] -> [b]
zapp (f : fs) (x : xs) = f x : zapp fs xs
zapp = [ ]

すると、transposeは次のようにあらわせます。

transpose :: [ [a] ]-> [ [a] ]
transpose [ ] = repeat [ ]
transpose (xs : xss) = repeat (:) ‘zapp‘ xs ‘zapp‘ transpose xss

思惑通りrepeatがうまく使えているのがわかります。そして、この様に
 zapp :: [a -> b] -> [a] -> [b]
といった形の糊で関数を適用できるクラスのコンテナ(ここではリストですね)をApplicativeと呼ぶようです。Applicativeのクラス定義は以下のようなものです:

class Applicative f where
	pure :: a -> f a
	<*> :: f (a -> b) -> f a -> f b

今のtransposeの例を拡張して、リストのApplicativeクラスインスタンスを考えれば、

instance Applicative [] where
	pure x = repeat x
	f <*> a = f `zapp` a

となり、transposeはさらに以下のようになります:

transpose :: [ [a] ]-> [ [a] ]
transpose [ ] = pure [ ]
transpose (xs : xss) = pure (:) <*> xs <*> transpose xss

そして、Monadと同様にApplicativeにも次の法則を満たすことが要求されています:

identity                   pure id <*> u = u
composition  pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
homomorphism         pure f <*> pure x = pure (f x)
interchange               u <*> pure x = pure (\f->f x) <*> u

identityはidがわかってしまえば当然ですね。compositionは (.) :: (b -> c) -> (a -> b) -> a -> cなので、まぁ、わからなくもないかなという感じですね…homomorphismとinterchangeもゴリゴリ展開すれば、ぁあ、そのとおりといった感じでしょう。

 Monad則にしてもApplicative則にしても、なぜクラス定義以外にこうやって「守ってくださーい」と呼びかけるだけのルールが出てくるのか不思議なのですが、何ででしょう?

 例えば、誰かがApplicativeのインスタンスクラスを定義して、例えば、その定義ではidentityルールが破綻していたとしたら、そのインスタンスクラスを利用して、Applicativeパターンを表現することはできなそうな感じです。つまり、クラスとそのメソッドの定義だけではクラスメソッドの振る舞いを限定できない。でも、そのクラスが表現しようとしているコードパターンはクラスメソッドの振る舞いに依存している。だからクラスメソッドに付け足す形で〜則がついているということなのでしょうか…

 クラス定義を見るだけではそのクラスに付随する則が見えないし、自分が書いたインスタンスクラスが間違っていても自分で検証するまでわからないのがもどかしい気がします...

今日は、このへんで...ではでは。