この前、知り合いに米田の補題を教えてもらって、ようやく腹落ちしました。
圏は群の拡張っぽい(違います) 圏
C \mathbf C C は
対象 O b j ( C ) \rm Obj(\mathbf C) Obj ( C ) を複数持ちます。
対象 X , Y \rm X, Y X , Y の間には
射 H o m ( X , Y ) \rm Hom(X,Y) Hom ( X , Y ) が複数あり、射をつなげる演算で閉じています。
射をつなげる演算は結合法則が成立します。
( f ▹ g ) ▹ h = f ▹ ( g ▹ h ) (f\triangleright g) \triangleright
h =
f\triangleright (g \triangleright
h ) ( f ▹ g ) ▹ h = f ▹ ( g ▹ h ) 始まりと終わりが同じ射
H o m ( X , X ) \rm Hom(X,X) Hom ( X , X ) には特別な恒等射
i d X \rm id_X i d X が存在し、射につなげると溶けて消えます。
f ▹ i d Y = f i d X ▹ f = f f\triangleright {\rm id_Y} = f\\ {\rm id_X}\triangleright
f=f
f ▹ i d Y = f i d X ▹ f = f 射の逆元は保証されないので、圏は群の拡張ではなくモノイドの拡張です。そういえば、競プロのセグメント木に載る構造はモノイドではなく正確には圏が載るという話がありましたね。
なお、普通は射をつなげる演算は
g ∘ f g \circ f g ∘ f で書きます。射をつなげる順序と関数適用の向きが逆でややこしいので、この記事では
f ▹ g f \triangleright g f ▹ g で書きます。あくまでローカルルールです。
例: 集合の関数の圏 集合を対象として、集合と集合の間の射を関数とすると圏をなします。
neg : Int -> Int
to_str : Int -> String
neg | > to_str : Int -> String
例: モノイドの圏 全てのモノイド
( M , ∘ ) (M, \circ) ( M , ∘ ) は圏として扱えます。対象をただひとつの
∗ \ast ∗ として、射
H o m ( ∗ , ∗ ) {\rm Hom}(\ast,\ast) Hom ( ∗ , ∗ ) をモノイド
M ∋ x M\ni x M ∋ x とします。射をつなげると演算をして
x ▹ y = x ∘ y x\triangleright y=x \circ y x ▹ y = x ∘ y の圏になります。
例: 順序の圏 全ての順序集合
( S , ≥ ) (S,\ge) ( S , ≥ ) は圏として扱えます。対象
x , y x,y x , y を集合の要素、射を順序
x ≥ y x\ge y x ≥ y が成り立つときだけある1つの射とすると、順序集合は圏になります。どんな2つの対象の間も射は0本か1本です。
関手は圏をまるごと移す 圏
C \mathbf C C の対象と射を圏の法則を保ったまま変化させる写像を
関手 F F F といいます。
F : C ⇒ D F : X ↦ F X F : f ↦ F f {\bm F}\colon{\bf C} \Rightarrow {\bf D} \\ {\bm F}\colon {\rm X} \mapsto {\bm F} {\rm X} \\ {\bm F}\colon f\mapsto {\bm F} f F : C ⇒ D F : X ↦ F X F : f ↦ F f 圏の規則を保ったまま移すために、射の写像に課される条件は次の2つです。
恒等射の保存 F i d X = i d F X {\bm F}{\rm id_X} = {\rm id}_{{\bm F}{\rm X}} F i d X = id F X 射をつなげる演算の保存 F ( f ▹ g ) = F f ▹ F g {\bm F}(f \triangleright g) = {\bm F}f \triangleright {\bf F} g F ( f ▹ g ) = F f ▹ F g 例: 有限集合の全射を自然数の順序に移す関手 C {\bf C} C : 有限集合を対象X , Y \rm X,Y X , Y として、全射の関数f : X → Y f: \rm X \to Y f : X → Y を射とする圏D \bf D D : 自然数を対象x , y \rm x,y x , y として、x ≥ y x \ge y x ≥ y なら射が1つだけある圏について要素数を対応させるの関手
F : C ⇒ D {\bm F}\colon \bf C \Rightarrow D F : C ⇒ D が作れる。
F ( X ) = ∣ X ∣ {\bm F}({\rm X})=|\rm X| F ( X ) = ∣ X∣ F ( f : X → Y ) = ∣ X ∣ ≥ ∣ Y ∣ {\bm F}(f\colon {\rm X}\to {\rm Y})=|X|\ge|Y| F ( f : X → Y ) = ∣ X ∣ ≥ ∣ Y ∣ を表す唯一の射全く別の2つの圏の関係を調べられるのが関手の嬉しさですね。
例: List<T>型 List : 型 T を型 List < T > にする。
map : 関数 ( a -> b ) を -> 関数 ( List < a > -> List < b > ) にする。
Hom関手は射を対象にする関手 Hom関手が出でてきたあたりから、ややこしくて苦手でした。
対象Xを
対象Aから始まりXで終わる射でつくられた集合 に移す関手がHom関手
H A : C ⇒ S e t {\bm H}^{\rm A}\colon {\bf C} \Rightarrow {\rm Set} H A : C ⇒ Set です。
H A ( X ) = H o m ( A , X ) {\bm H}^{\rm A}({\rm X})= {\rm Hom}(\rm A,X) H A ( X ) = Hom ( A , X ) 例えば集合の圏の場合、Hom関手
H I n t {\bf H}^{\rm Int} H Int は集合
X {\rm X} X を「Intを引数に取る(
I n t → X \rm Int \to X Int → X )全ての関数の集合」に移します。
注意: 圏の射が集合をなすとは限らないのですが、ここではそう扱います。
Hom関手
H A {\bm H}^{\rm A} H A は射
f f f を「関数から関数に移す関数」に移します。
H A ( f : X → Y ) = σ ↦ σ ▹ f H A : ( X → Y ) → ( H o m ( A , X ) → H o m ( A , Y ) ) {\bm H}^{\rm A}(f\colon {\rm X \to Y}) = \sigma \mapsto \sigma \triangleright
f \\ {\bm H}^{\rm A}\colon({\rm X\to Y})\to ({\rm Hom(A,X) \to Hom(A,Y)}) H A ( f : X → Y ) = σ ↦ σ ▹ f H A : ( X → Y ) → ( Hom ( A , X ) → Hom ( A , Y ) ) 射を写像の型は複雑ですが、定義はシンプルです。「Aを引数に取る関数σ」の終わりに「与えられた射f」をぶっ刺す関数です。
Hom関手
H I n t {\bf H}^{\rm Int} H Int の場合、関数f: Bool→Stringを移すと(Int→Bool)→(Int→String)になります。
HInt : ( X -> Y ) -> ( Int -> X ) -> ( Int -> Y )
HInt ( f ) = fun σ -> σ | > f
to_str : Bool -> String
g ( b ) = if b { "T" } else { "F" }
is_large : Int -> Bool
h x = x > 100
HInt to_str :: ( Int -> Bool ) -> ( Int -> String )
HInt to_str is_large x
== ( fun σ -> σ | > to_str ) is_large x
== ( is_large | > to_str ) x
== to_str ( x > 100 )
== if x > 100 { "T" } else { "F" }
半変関手のHom関手 Intを引数に取るHom関手があるなら、Intを返すHom関手もあります。
H A ( X ) = H o m ( X , A ) {\bm H}_{\rm A}({\rm X})= {\rm Hom}(\rm X,A) H A ( X ) = Hom ( X , A ) 射を移すときは頭にぶっ刺します。
H A ( f : X → Y ) = σ ↦ f ▹ σ H A : ( X → Y ) → ( H o m ( Y , A ) → H o m ( X , A ) ) {\bm H}_{\rm A}(f\colon {\rm X \to Y}) = \sigma \mapsto f \triangleright \sigma \\ {\bm H}_{\rm A}\colon({\rm X\to Y})\to ({\rm Hom(Y, A) \to Hom(X,A)}) H A ( f : X → Y ) = σ ↦ f ▹ σ H A : ( X → Y ) → ( Hom ( Y , A ) → Hom ( X , A ) ) ところが、ここで1つ問題があります。射を移す写像の返す射の向きが逆向きになっています。本来の関手
F : C ⇒ D {\bm F}\colon {\bf C \Rightarrow D} F : C ⇒ D であれば
f : X → Y f\colon {\rm X \to Y} f : X → Y を
F X → F Y {\bm F}{\rm X}\to {\bm F}{\rm Y} F X → F Y に移しますが、
H A {\bm H}_{\rm A} H A は
F Y → F X {\bm F}{\rm Y}\to{\bm F}{\rm X} F Y → F X に移します。
このような、射を逆向きにして移す関手を
反変関手 F : C o p ⇒ D {\bm F}: {\bf C}^{\rm op}\Rightarrow{\bf D} F : C op ⇒ D として扱います。
H A : C o p ⇒ S e t {\bm H}_{\rm A}:{\bf C}^{\rm op}\Rightarrow {\bf Set} H A : C op ⇒ Set です
例: べき集合と逆像の反変関手 べき集合関手
P : S e t o p ⇒ S e t \bm P\colon\bf Set^{\rm op} \Rightarrow Set P : Se t op ⇒ Set を次のように定義します。
P ( X ) = { X ′ ∣ X ′ ⊂ X } P ( f ) = f − 1 = Y ↦ { x ∣ f ( x ) ∈ Y , x ∈ X } {\bm P}({\rm X}) = \{X' | X' \subset X\} \\ {\bf P}(f)=f^{-1}={\rm Y}\mapsto \{x | f(x) \in {\rm Y}, x \in {\rm X}\} P ( X ) = { X ′ ∣ X ′ ⊂ X } P ( f ) = f − 1 = Y ↦ { x ∣ f ( x ) ∈ Y , x ∈ X } 集合をべき集合に移し、関数は逆像に移します。このとき、べき集合関手
P \bm P P は反変関手になります。
自然変換は2つの関手で移した先を移す 関手
F , G : C ⇒ D {\bm F},{\bm G}\colon {\bf C \Rightarrow D} F , G : C ⇒ D の間の関係が
自然変換 θ : F ⇝ G \theta\colon {\bm F}\leadsto
{\bm G} θ : F ⇝ G です。自然変換の中身は関手の元の圏
C \bf C C の対象から
D \bf D D の射を返す写像です。対象に対する射を自然変換の成分
θ X = f : F X → G X \theta_{\rm X} = f\colon {\bf F
}{\rm X}\to{\bf G}{\rm X} θ X = f : F X → G X と書きます。
関手で移した射と自然変換の成分について、射の交換が成立する必要があります。これを自然性と言います。
F f ▹ θ Y = θ X ▹ G f {\bm F}f\triangleright\theta_{\rm Y}=\theta_{\rm X}\triangleright{\bm G}f F f ▹ θ Y = θ X ▹ G f ※
⇝ \leadsto ⇝ の矢印もメジャーな記法ではないです。
例: List<T>とOption<T>の間の多相関数 例えば、List<T>→Option<T>の多相head関数はList→Optionの自然変換List~>Optionになります。
head < T > : List < T > -> Option < T >
head ( l ) = if l . size ( ) > 0 { Some ( l [ 0 ] ) } else { None }
fmap < F > : ( X -> Y ) -> ( F X -> F Y )
to_str : Int -> String
fmap < List > to_str | > head < String > :: List < Int > -> Option < String >
( fmap < List > to_str | > head < String > ) [ 1 , 2 ]
== head < Int > ( [ "1" , "2" ] )
== Some ( "1" )
head < Int > | fmap < Option > to_str :: List < Int > -> Option < String >
( head < Int > | fmap < Option > to_str ) [ 1 , 2 ]
== ( fmap < Option > to_str ) ( Some ( 1 ) )
== Some ( "1" )
米田の補題 関手、Hom関手、自然変換が出てくる有名な補題が米田の補題です。
米田の補題は次の2つの間に全単射
y y y が存在することです。
圏
C \bf C C の対象
A \rm A A と反変関手
F : C o p ⇒ S e t {\bm F}\colon{\bf C}^{\rm op}{\bf \Rightarrow Set} F : C op ⇒ Set について、
θ : H A ⇝ F \theta: {\bm H}_{\rm A}\leadsto{\bm F} θ : H A ⇝ F (Aに行くHom関手から関手Fへの自然変換)F A {\bm F}{\rm A} F A (関手で移した先の対象)……なんか突然いっぱい文字が出てきて困りました。集合の圏の例でなぞってみましょう。
※ 異なる対象A,B、射
f : A → B f\colon\rm A\to B f : A → B について全単射が
y A y_{\rm A} y A y B y_{\rm B} y B が自然になる
F f ▹ y A − 1 = y B − 1 ▹ G f \bm Ff\triangleright y_{\rm A}^{-1}=y_{\rm B}^{-1}\triangleright \bm Gf F f ▹ y A − 1 = y B − 1 ▹ G f ことも補題の一部です。
G \bm G G は対象を自然変換に移す関手で、
f : A → B f\colon\rm A \to B f : A → B を
G f : ( ϕ : H B ⇝ F ) → ( θ : H A ⇝ F ) {\bm G}f\colon(\phi:{\bm H_{\rm B}}\leadsto \bm F)→(\theta:{\bm H_{\rm A}}\leadsto {\bm F}) G f : ( ϕ : H B ⇝ F ) → ( θ : H A ⇝ F ) に移します。
※ 同様に、異なる関手
F , G {\bm F,\bm G} F , G 、自然変換
ψ : F ⇝ G \psi:\bm F\leadsto\bm G ψ : F ⇝ G についても自然
ψ A ▹ y G − 1 = y F − 1 ▹ Ψ A \psi_{\rm A}\triangleright y^{-1}_{\bm G}=y^{-1}_{\bm F}\triangleright \Psi_{\rm A} ψ A ▹ y G − 1 = y F − 1 ▹ Ψ A です。
Ψ A : ( θ : H A ⇝ F ) → ( ϕ : H A ⇝ G ) \Psi_A: (\theta\colon \bm H_
{\rm A}\leadsto F) \to (\phi\colon\bm H_{\rm A}\leadsto G) Ψ A : ( θ : H A ⇝ F ) → ( ϕ : H A ⇝ G ) です。
例: べき集合関手 C = S e t {\bf C} = {\bf Set} C = Set A = { 0 , 1 } {\rm A} = \{0, 1\} A = { 0 , 1 } F : S e t o p ⇒ S e t {\bm F}\colon {\bf Set}^{\rm op}\Rightarrow {\bf Set} F : Set op ⇒ Set べき集合関手とすると、
θ : H { 0 , 1 } ⇝ P \theta\colon{\bm H}_{\rm \{0,1\}} \leadsto {\bm P} θ : H { 0 , 1 } ⇝ P ({0,1}に行くHom関手からべき集合関手への自然変換)F A = P { 0 , 1 } = { ∅ , { 0 } , { 1 } , { 0 , 1 } } {\bm F}{\rm A} = {\bm P}\{0,1\} = \{\varnothing, \{0\}, \{1\}, \{0,1\}\} F A = P { 0 , 1 } = { ∅ , { 0 } , { 1 } , { 0 , 1 }} が1対1に対応すると言っているらしいです。
{0,1}に行くHom関手からべき集合関手への自然変換
θ \theta θ って具体的に何でしょうか。自然変換の定義を思い出すと、Setの対象XからSetの射
θ X \theta_{\rm X} θ X が得られるものでした。Setの射は関数です。つまり、Xから{0,1}を返す関数
σ \sigma σ を得て、Xのべき集合を返す多相関数が自然変換です。
θ X : ( X → { 0 , 1 } ) → P X \theta_{\rm X} : (X \to \{0,1\})\to{\bm P}{\rm X} θ X : ( X → { 0 , 1 }) → P X 米田の補題の自然変換で対象からSetの射
θ X : S e t → S e t \theta_{\rm X}\colon {\bf Set \to Set} θ X : Set → Set が得られるのは、べき集合関手に限らず一般にそうです。
Setの射は関数なので要素を関数適用できる。 ここが僕の理解できていなかったポイントでした。
全単射yを実際に構成する 全単射
y = θ ↦ a : ( H A ⇝ F ) → F A y=\theta \mapsto a \colon ({\bm H}_{\rm A}\leadsto {\bm F}) \to {\bm F}{\rm A} y = θ ↦ a : ( H A ⇝ F ) → F A を実際に作ってみましょう。
対象
X \rm X X について自然変換の成分
θ X : H A X → F X \theta_{\rm X}\colon{\bm H}_{\rm A}{\rm X}\to{\bm F}{\rm X} θ X : H A X → F X とします。成分は先ほどの通り関数です。
自然変換の成分のうち
X = A {\rm X} = {\rm A} X = A としてみます。関数
θ A : H A A → F A \theta_A \colon {\bm H}_{\rm A}{\rm A} \to {\bm F}{\rm A} θ A : H A A → F A が得られます。
H A A = H o m ( A , A ) {\bm H}_{\rm A}{\rm A} = {\rm Hom(A,A)} H A A = Hom ( A , A ) のことでした。圏には恒等射
i d A : A → A {\rm id_A}:{\rm A \to A} i d A : A → A が存在します。Setの恒等射は恒等関数です。
自然変換の成分に恒等関数を適用して
θ A ( i d A ) \theta_{\rm A}({\rm id_A}) θ A ( i d A ) ができます。得られた結果は
F A {\bm F}{\rm A} F A の対象になっています。やりました!
y ( θ ) = θ A ( i d A ) y(\theta)=\theta_{\rm A}(\rm id_A) y ( θ ) = θ A ( i d A ) が全単射を与えてくれるようです。
べき集合関手の例の続き べき集合関手では
y ( θ ) = θ A ( i d A ) y(\theta)=\theta_{\rm A}(\rm id_A) y ( θ ) = θ A ( i d A ) はどうなるでしょうか。
θ X \theta_X θ X はXから{0,1}を返す関数
σ \sigma σ を得て、Xのべき集合を返す多相関数でした。Xから{0,1}を返す関数
σ \sigma σ をみて、1を返す要素を集めて返す関数が自然変換になりそうです。
θ X ( σ : X → { 0 , 1 } ) = { x ∈ X ∣ σ ( x ) = 1 } \theta_{\rm X}(\sigma\colon {\rm X}\to\{0,1\})=\{x\in X|\sigma(x)=1\} θ X ( σ : X → { 0 , 1 }) = { x ∈ X ∣ σ ( x ) = 1 } θ A ( i d A ) \theta_{\rm A}(\rm id_A) θ A ( i d A ) を考えてみると、
θ { 0 , 1 } ( σ : { 0 , 1 } → { 0 , 1 } ) = { x ∈ { 0 , 1 } ∣ σ ( x ) = 1 } θ { 0 , 1 } ( i d A ) = { 1 } \theta_{\{0,1\}}(\sigma:\{0,1\}\to\{0,1\}) = \{x \in \{0,1\} | \sigma(x)=1\}\\\theta_{\{0,1\}}({\rm id_A})=\{1\} θ { 0 , 1 } ( σ : { 0 , 1 } → { 0 , 1 }) = { x ∈ { 0 , 1 } ∣ σ ( x ) = 1 } θ { 0 , 1 } ( i d A ) = { 1 } 1を返す要素を集めて返す多相関数
θ \theta θ は、
{ 1 } ∈ P { 0 , 1 } \{1\}\in{\bf P}\{0,1\} { 1 } ∈ P { 0 , 1 } と対応しているようです。
逆向きの全単射とべき集合関手 ところで、米田の補題は全単射を与えてくれます。つまり、
P { 0 , 1 } = { ∅ , { 0 } , { 1 } , { 0 , 1 } } {\bm P}\{0,1\} = \{\varnothing, \{0\}, \{1\}, \{0,1\}\} P { 0 , 1 } = { ∅ , { 0 } , { 1 } , { 0 , 1 }} に対応して、多相関数
θ \theta θ が得られるようです。
y − 1 = a ↦ θ : F A → ( H A ⇝ F ) y^{-1}=a \mapsto \theta \colon {\bm F}{\rm A} \to ({\bm H}_{\rm A}\leadsto {\bm F}) y − 1 = a ↦ θ : F A → ( H A ⇝ F ) を具体的に与えみましょう。
全ての対象Xについて、自然変換の成分
θ X : H A X → F X \theta_{\rm X}\colon {\bm H}_{\rm A}{\rm X}\to{\bm F}{\rm X} θ X : H A X → F X を与えれば自然変換を定義したことになるのでした。そして、成分は関数です。
AからXへの射
σ ∈ H A X = H o m ( X , A ) \sigma \in {\bm H}_{\rm A}{\rm X}={\rm Hom(X,A)} σ ∈ H A X = Hom ( X , A ) を
θ X \theta_{\rm X} θ X の引数に得たとき、反変関手
F : C o p ⇒ S e t \bm F\colon {\bf C}^{\rm op}\Rightarrow {\bf Set} F : C op ⇒ Set で
σ \sigma σ を移すと関数
F σ {\bm F}\sigma F σ が得られます。
σ : X → A F σ = F A → F X \sigma: X\to A\\{\bm F}\sigma = {\bm F}{\rm A}\to{\bm F}{\rm X} σ : X → A F σ = F A → F X F σ ( a ) {\bm F}\sigma(a) F σ ( a ) を計算すれば、無事に
F X {\bm F}X F X が得られました。
y − 1 ( a ) X = σ ↦ F σ ( a ) y^{-1}(a)_{\rm X}=\sigma \mapsto {\bm F}\sigma(a) y − 1 ( a ) X = σ ↦ F σ ( a ) で良さそうです。
べき集合関手の例で
{ 0 } ∈ P { 0 , 1 } \{0\}\in{\bm P}\{0,1\} { 0 } ∈ P { 0 , 1 } としたときの自然変換
θ \theta θ は次のようになります。
y − 1 ( { 0 } ) X = σ ↦ P σ ( { 0 } ) = { x ∈ X ∣ σ ( x ) = 0 } y^{-1}(\{0\})_{\rm X}=\sigma \mapsto {\bm P}\sigma(\{0\})=\{x\in X | \sigma (x) =0\} y − 1 ({ 0 } ) X = σ ↦ P σ ({ 0 }) = { x ∈ X ∣ σ ( x ) = 0 } σ : X → { 0 , 1 } \sigma:{\rm X}\to\{0,1\} σ : X → { 0 , 1 } について、0を返す要素の集合を返す多相関数が
θ X \theta_{\rm X} θ X のようです。1を返す要素を集めて返す関数とよく似ていますね。
P { 0 , 1 } = { ∅ , { 0 } , { 1 } , { 0 , 1 } } {\bm P}\{0,1\} = \{\varnothing, \{0\}, \{1\}, \{0,1\}\} P { 0 , 1 } = { ∅ , { 0 } , { 1 } , { 0 , 1 }} に対応して、関数
σ \sigma σ がその要素を返す要素を集めると、それぞれ自然変換が手に入るようです。
米田の補題の嬉しさ 米田の補題は、
F = H B : C o p ⇒ S e t {\bm F}={\bm H}_{B}\colon{\bm C}^{\rm op}\Rightarrow{\bf Set} F = H B : C op ⇒ Set とした形がよく利用されます。
y : ( H A ⇝ H B ) → H B A ( = H o m ( A , B ) ) y\colon ({\bm H}_{\rm A}\leadsto{\bm H}_{\rm B})\to {\bm H}_{\rm B}A\ (={\rm Hom(A,B)}) y : ( H A ⇝ H B ) → H B A ( = Hom ( A , B ) ) 自然変換
θ \theta θ とAからBへ行く射
f ∈ H o m ( A , B ) f\in {\rm Hom(A,B)} f ∈ Hom ( A , B ) が1対1に対応するようです。ポイントは、射
f f f は関数とは限らない謎の矢印であるのに、
自然変換の成分 θ X : H A X → H B X \theta_X\colon{\bm H}_{\rm A}{X}\to{\bm H}_{\rm B} X θ X : H A X → H B X は関数 であるということです。Xから始まる射の集合とその間の関数を調べるだけで、謎の射の矢印のことがわかるのがうれしいですね。
米田の補題は型の圏だと contramap : ( X -> Y ) -> ( F Y -> F X )
A = Int とする。
F を反変関手とし、contramapがある。
y : ( forall X . ( X -> Int ) -> F X ) -> F Int
y ( σ ) = σ < Int > ( id < Int > )
y_ : F Int -> ( forall X . ( X -> Int ) -> F X )
y ( a ) = fun σ -> contramap < F > ( σ ) ( a )
F = H B : C o p ⇒ S e t {\bm F}={\bm H}_{B}\colon{\bm C}^{\rm op}\Rightarrow{\bf Set} F = H B : C op ⇒ Set 版だと、
F = * -> String とすると
y : ( forall X . ( X -> Int ) -> ( X -> String ) -> Int -> String
y ( σ ) = σ < Int > ( id < Int > )
y_ : ( Int -> String ) -> ( forall X . ( X -> Int ) -> ( X -> String )
y_ ( f ) = fun σ -> contramap < * -> String > ( σ ) ( f )
contramap < * -> Sring > : ( X -> Y ) -> ( Y -> String ) -> ( X -> String )
contramap ( σ ) ( f ) = σ | > f
y_ ( f ) = fun σ -> σ | > f
具体的にto_str: Int→Stringを与えると、(X → Int) → (X → String)に変換できる。
to_str : Int -> String
y_ ( to_str ) : ( X -> Int ) -> ( X -> String )
y_ ( to_str ) == fun σ -> σ | > to_str
map_to_str : ( X -> Int ) -> ( X -> String )
map_to_str ( σ ) = fun x -> to_str ( σ ( x ) ) == σ | > to_str
y ( map_to_str ) : Int -> String
y ( map_to_str ) == map_to_str ( fun x -> x ) == fun x -> to_str ( x )
ところで、残念ながらHaskellの反変関手Data.Functor.Contravariantにはほとんど例がない。
数少ない中でEquivalenceがそうらしい。引数が型変数だと反変になりがち。
newtype Equiv a = Equiv { getEquiv :: a -> a -> Bool }
contramap < Equiv > : ( X -> Y ) -> ( Equiv Y ) -> ( Equiv X )
contramap ( σ ) = fun e -> Equiv ( fun x y -> getEquiv e ( σ x ) ( σ y ) )
リストの先頭だけで等価判定
head_eq = Equiv ( List Int )
head_eq = Equiv ( fun x y -> head x == head y )
タプルの 1 つ目をリストにする関数で
fst_to_init :: ( Int , a ) -> List Int
fst_to_init ( x , _ ) = [ x ]
タプルの 1 つ目で判定する。やはり、頭にぶっ刺して、先に処理する。
fst_eq :: Equiv ( Int , a )
fst_eq = contramap fst_to_init
FunctorなF AはAを与えてくれるので、そこに関数A→Bを適用できる。ContravariantなF AはAを消費するので、その前に関数B→Aを処理することができる。という関係がありますね。
参考文献 次のPDF群を大いに参考にしました。べき集合関手と補集合の例がとくにわかりやすいです。
Equivalenceの例はここから。