Applicativeの変わった仲間たち
Applicativeネタ、つきません...ほかにも見るところ、いっぱいあるんですが…
ということで、コレハ、いったい、どう読めば良いのでしょう:
instance Applicative ( (->) a) where pure = const (<*>) f g x = f x (g x) instance Monoid a => Applicative ( (,) a) where pure x = (mempty, x) (u, f) <*> (v, x) = (u `mappend` v, f x)
まずは、一つ目のほう。補足情報として:
const :: a -> b -> a
つまり、constは第2パラメタが何であろうと、必ず第一パラメタの値を返す関数。(ちょっと前までだったら、こんなろくでもない関数に用途があることすら信じられなかったと思います...)
そして、Hoogleで->を調べると->は予約語の一つで、"function type constructor"だということです。Kind Levelでは
(->) :: ?? -> ? -> *
なのだそうです…Kindって何じゃ?と思うのですが、じつは、今まで色々実験している間、時々Kindエラーというのをもらっていました…そこは、とりあえず今は踏み込まないとして、もう一つのヒントは、->が"function type constructor"だということです。
タイプコンストラクタといえば、
Data Tree a = Leaf a | Node (Tree a) a (Tree a)
で言うところのLeafやNodeTreeにあたるわけです…それの定義が括弧でくくってあるということは、これは中置型のタイプコンストラクタということなのかもしれません…(乱暴ですが…)だとすれば、((->) a)はaを第一パラメタにとる関数ということ?(追記:Haskell98レポートによれば、->は唯一の中置型(infix)タイプコンストラクタであるとのことです)
GHCiで、:?とやってみると、:kindというコマンドがあることに気づきます。ちなみに":kind (->)"とやってみると、Hoogleが教えてくれたように
(->) :: ?? -> ? -> *
と返してくれます。さらに、
:kind ( (->) Int) ( (->) Int) :: ? -> * :kind ((->) Int Int) ( (->) Int Int) :: *
だそうです。最後に挙げた((->) Int Int)はつまり(Int -> Int)なわけで、IntをとってIntを返す関数なわけですから、型は確定してますね…つまり、::*というのはKindの中では型が確定しているという意味なのかもしれません。(説明を見たほうがはやい?)
つぎに、pureをもう少し見てみます。
pure = const
pure x = const x
ここで、
const :: a -> b -> a
const a b = a
ですから、
pure x = \_ -> x
見たいな感じでしょうか…
つぎに、<*>を見ます。なんで、<*>が3ヶもパラメタをとるのでしょう?頭が痛くなってきました…でも、このApplicativeが関数をつなげるものだとすれば、下のfもgもa型の第一パラメタにxを取る関数なのだろうということが考えられます…だとすれば:
(<*>) f g x = f x (g x)
と書かれている以上、f xは少なくともパラメタを一つとる関数である必要がありますね…
(<*>) f g x = f x (g x) || (<*>) :: (a -> b -> c) -> (a -> b) -> a -> c
といった感じなのでしょうか…ここで、<*>の第一パラメタがpureの結果だった場合を考えて見ます。パラメタは全て関数でなくてはいけないので、こんな例をやって見ます。
pure (+1) <*> (+3) = const (+1) <*> (+3) = (\_ -> (+ 1) ) <*> (+ 3) =\x -> (+1) (x + 3) = x + 1 +3 (pure (+1) <*> (+3) ) 1 = 5
どうやら、関数のためのコンビネータのようなものらしいですねぇ…ちなみに、(+3)`が<*>の右辺でも使えるのだから、左辺にもpureなしで関数を指定できそうです。でも、その場合は<*>の右辺よりも一つ多くパラメタをとる必要がありそうです…
(+) <*> (+3) = \x -> (+ x) (x + 3) = \x -> (x+x+3) ( (+) <*> (+3) ) 2 = 7 ( (:) <*> (:[ ]) ) 1 = [1, 1]
<*>でたくさんつなげたときはどうなるのでしょう?
(f1 <*> f2 <*> f3 <*> f4) x = ( ( (f1 x) f2 x) f3 x)(f4 x) ただし f1 :: a -> b -> c -> d -> e f2 :: a -> b f3 :: a -> c f4 :: a -> d
ということらしいです...こうやって見ると、ほかの例で見たApplicativeパターンと同じで、constを使ったpureは関数をApplicativeパターンにリフトする効果があって、<*>は可変数のパラメタをリフトされた関数に渡す効果があることがわかります...
ここまでやってからApplicativeの論文に戻ると、
instance Applicative ( (->) env) where pure x = \_-> x ef <*> ex = \env -> (ef env) (ex env)
ということで、Stateモナドのように、コンテキストをとる関数をバインドさせるためのもののようです。しかしながら、Applicative ((->) a)でのコンテキストaは参照しかできないようですね...そもそもApplicative ((->) a) の<*>はStateモナドのように変化するコンテキスト情報を式の左から右に流してくれる機能はないですね...<*>が左辺から評価するようにできているようなので、実行順序は式の中では決定しているように見えます。型表記が不思議なところに興味を惹かれましたが、実際のところ、便利に使える局面はどれくらいのものなんでしょうか…(追記:ポストを書いた後色々考えたのですが、これはこれでなかなか使い出があるでしょう。件の論文のほうではこれを使って簡単なインタープリタを作っています。何か面白いものが書けたらまたその話をしたいと思います。)
2つ目のほうは、ながくなっちゃいましたので、また日を改めて…
ではでは...