Hom関手と関数型(->)
Hom関手の良い例として関数型 (->)
が考えられる
これは型引数を2つ取る型コンストラクタであり、
2つのうちのいずれかを固定することで関手になることができる
Reader e a
で考えても同じだが、関数型で考えたほうが直感的でわかりやすいと思う

まずはドメイン側を固定したものを見る
対象としての型がいくつか考えられるが、今回は Int
をドメインとしての対象として固定する
すると関手 (->) Int
を考えられる
Int
とは別の型 X
を決めるごとに、新たな型 (->) Int X
が得られる
(->) Int X
はHaskellにおける型であるので、Hask圏の対象の一つである
(->) Int X
は Int -> X
と表記することもでき、全く同じ意味であり、今回は前者を用いる
と、言いながら↓の図では後者で描いてるな

ここで例として Bool
と Char
を置いてみた
(->) Int
を関手と見ることで、 (->) Int Bool
と (->) Int Char
という対象への対応が取れる
(->) Int
関手はドメインもコドメインも同じHask圏であるので、これは自己関手の一種となる
対象についての対応はわかったので、射の対応についてみてみよう
射の対応は、ここでは
Int -> Bool
という型の関数を一つ選択して、
f :: Bool -> Char
と合成すると、
Int -> Char
という型の関数となり
その対応が (->) f
、つまり fmap f
になるのだった
どういうことか?具体的に見ていこう
Int -> Bool
という型の関数もいくつも考えられる
odd
even
isNegativeZero
etc.
同様に Int -> Char
という型の関数もいくつも考えられる
intToDigit
chr
etc.
ここで、 f
を以下のように定義する
hsf :: Bool -> Char
f True = 't'
f False = 'f'
例えば、 Int -> Bool
という型の関数から、 even
を選び、 f
と合成する
この f . even
は Int -> Char
という型の関数群の一つである
f . even
と全く同じ挙動をする関数は関数合成を用いなくとも定義できる
hsfev :: Int -> Char
fev n = if odd n then 'f' else 't'
これが (->) f
の対応である
つまり、Haskの写し先では
(.) f
という関数を、 even
に適用すると、 fev
が出てくる
イメージ的には ((.) f) even == fev
関数型 (->) r
のFunctorの定義は以下のようなものだった
hsinstance Functor ((->) r) where
fmap = (.)
定義にもあるように fmap
は、関数合成 .
で定義されている
写し先のHask圏で成り立っているかを確認しよう
省略するが、残りの関手の定義である合成の保存と恒等射の保存も成り立つ
以上により、 (->) Int
は、Hask圏におけるHom関手であることがわかった
以下ではコドメイン側を固定した関手 (->) _ Int
を考える
(->)
や (,)
は1つ目、2つ目のどちらを固定するかで共変か反変かが変わる
「2つの型引数を取る型コンストラクタ」ではなく、↑これら2つに関しての話か。

1つ目を固定した場合
例えば (->) Int
という1つの型引数を取る関手を考える
これは、共変関手となる
これは上の普通の関手の議論と同じなので省略
2つ目を固定した場合
例えば (->) _ String
という1つの型引数を取る関手を考える
これは、反変関手になる
共変関手だと考えるとどういった不都合が生じるの?