LoginSignup
1
1

More than 5 years have passed since last update.

ファンクターとしての函数型構築子

Last updated at Posted at 2013-12-08

函数型コンストラクタ (->)

型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則についてはまた今度。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1