函数型コンストラクタ (->)
型aから型bへの函数の型はa->bである。だから、実は(->)は型aと型bを取って、a->bという型を作り出す二項型構築子だよね、と言われて面食らう。言われてみりゃそりゃそうだ。型構築子(->)が実はFunctorでありApplicatveであり(そしてMonadでもある)という話を、写経がてら自分のために整理しておく(なお文系情弱なので正確さは保証できない)。
Functorという型クラスに属する型fは:
fmap :: (b->c) -> (f b -> f c)
を備えていなければならない。だから、fは型bを引数に取る項数1の型構築子でなければならない。型構築子(->)は項数2なので、このままではFunctor型クラスを例化できない。そこで部分適用を使って、函数型a->bを((->) a)という単項型構築子が型bを取ったものと考える。そうすると、fを((->) a)と見て、
fmap :: (b->c) -> ((-> a) b -> (-> a) c)
つまり
fmap :: (b->c) -> ((a->b) -> (a->c))
というfmapがあればいい。これはつまりfmapが(b->c)型の函数に対して、(a->b)型の函数をとって(a-c)型の函数を返すような、ある函数を返してくれる、ということだ。抽象的すぎてわかりにくいが、函数合成演算子(.)の型を思い出すと:
(.) :: (b->c) -> (a->b) -> (a->c)
で、b->c型の函数とa->b型の函数を取ってa->c型の函数を返してくれるのだったが、これは言い換えれば、b-c型の函数を取って、a->b型の函数をa->c型の函数に変換するような、ある函数を返してくるということである。つまり型の関係としてはfmapと(.)は同じである。で、実はこれが((->) a)型の持つfmapの正体である。Functor Lawの第1則を確認しておくと:
fmap id = id
だということになる(ただし、最初のid函数の型は(b->b)であり、後者のid函数の型は((a->b)->(a->b))である)。id函数をfmapによってたとえばeven函数(不正確だがとりあえずその型をInt->Boolとしておく)に作用させてみよう:
fmap id even = id even -- ということは(.)がfmapであるためには
id.even = id even -- が成り立たなければいけない
id (::Bool->Bool) . even (::Int->Bool) =
id (::(Int->Bool)->(Int->Bool)) even (::Int->Bool)
が成立しなければいけないが、id.evenがevenと等価なのは当然だし(evenが返す値がidに渡されて同じ値がそのまま返ってくるだけだから)、他方でid evenはもちろんevenと等価なので(idは自身の引数である函数evenそれ自体をそのまま返すから)、id.evenと id evenは等価である。これはevenのみならず同様にして一般の函数について確かに成り立つ。第2則は任意のファンクター値h(ここではaを引数とする函数)に対して:
fmap (f.g) h = (fmap f . fmap g) h = fmap f (fmap g h)
で、fmapを(.)で置き換えてやれば
(f.g).h = f.(g.h)
で、これは函数合成の結合法則そのものなので当然成立している。ということで、函数型はファンクターであり、そのfmapは函数合成演算子(.)そのものなのであった。Applicative則についてはまた今度。