Cartesian Closed Categoryを駆け足で
森口
今回の発表の目的
圏(Category)の一例としてCartesian Closed Categoryを学ぶ。
圏論(Category theory)の勉強としては限定的に。
必要な内容を引っ張ってきてそれだけを。
一般に説明される構造の大半は触れない。
以下、ほとんどを日本語の用語で説明。
初出時は英語も併記。
Cartesian Closed Categoryは略称(CCC)を使用。
いい表記がみつからなかった。デカルト閉圏とかカルテシアン閉圏とか。長い。
余談:余談について
途中途中で、「余談:~~」というタイトルで 私の雑感が入る。
雑学じみていたり、どうでもいいことを言ったりする。つまり、無視して良い部分。
途中から口語になったりする。
主に私の集中力が切れたことを表す。
目次
圏論とは?
Cartesian Closed Category (CCC)とは?
単純型付きラムダ計算とは?
実際の対応関係は?
例としては?
圏論 概要 まあ、何かをする前にそれが何かをしらないと、話にならないよね。
圏論
圏(Category)という形で抽象化された体系
Category : 圏、と訳される。体系。
Category = Objects + Arrows
記号:C, D, ...
Object : 対象、と訳される。もの。
記号:A, B, C, D, ...
Arrow : 射、と訳される。morphismともいう。矢印。 対象間を結ぶ。
記号:f, g, h, ...
f : A → Bのように表記。(fはAからBへの射)
AとBをそれぞれドメイン(domain)、コドメイン(codomain)と呼ぶ。
満たさなければならない性質
射について満たさなければならない性質
全ての対象Aに対して、恒等射idA : A → Aが存在
全ての射f : A → B, g : B → Cに対して、g○f : A → Cが存在
射の合成の存在
全ての射f : A → B, g : B → C, h : C → Dに対して、h○(g○f) = (h○g)○f
射の合成の結合則
全ての射f : A → Bに対して、idB○f = f = f○idA
対象?特にないよ。
図式で表現1
f : A → Bを図式で書くと下の通り。
A
B
f
図式で表現2
g : B → Cとfを並べて書く。
A
B
f
C g
図式で表現3
h : C → Dを含むと下のようになる。
A
B
h
C g
D
f
図式で表現4
k : A → Dを追加する。
実はこれで、h○(g○f) = k = (h○g)○fである。
A
B
h
C
k D
f
g
図式の可換性
図式中で、ある始点から終点までを結ぶ射の列は、(複数経路ある場合)どの経路をたどっても合成結果が等しい。
図式が可換である(commute)という。
A
B
h
C
k D
f
g
結合則について
本来不要だが、g○fとh○gを書き加える。
ただの合成なので図式にいれられる。
わかりやすいでしょ?
A
B
h
C
k D
f
g
圏で何かを表現する
対象と射に、何か意味のあるものを割り当てる。
対象を要素、射を要素間の関係とする、など。
対象や射の性質を定義(設定)する。
e.g.) ある対象A, Bに対してf : A → Cおよびg : B → Cが唯一存在する。
この例に特に意味はない。
最終的に、対象と射の間に成立する関係によって性質を言及する。
実際にある圏
Set
対象を集合、射を(集合間の)全域関数とする圏。
恒等射は恒等関数、射の合成は関数合成、結合則は関数合成の性質。
Poset
対象を半順序集合、射を単調関数とする圏。
Cat
対象を(小さな)圏、射をFunctorとする圏。
圏の圏。Functorは圏から圏への変換と思ってくれれば。
圏論の教科書について
多くの本では、圏の各構造をボトムアップに定義していく。
実例が遠い・・・
一応途中実例は載っているが、全体がないのでピンとこない。
ここではトップダウンに、「Cartesian Closed Categoryに必要な要素を出て来た順に説明」する。
できる限り説明がネストしないように気をつける。
CARTESIAN CLOSED CATEGORY
本題。すぐ話が変わるけどね。ちなみに大文字にした覚えはないよ。
Cartesian Closed Category (CCC)
Cartesian Closedな圏(≠ある決まった圏)
終対象1が存在
二つの対象A, Bに対してA×Bが存在
積(product)
二つの対象B, Cに対してCBが存在
冪(exponential)
これらの説明は必要になるまでしません!(キリッ
余談:CCCという略称
Google先生は和洋問わず、いろいろ聞いたこともないようなものを出してくれる。
Wikipedia先生は
日本語版は残念ながら項目が少なく、Cartesian Closed Categoryはない。
そして私の愛する(してないけど)Cross-cutting concernsもない!
英語版は大量の項目があり、下の方へ行くとmathematicsのところにある。
・・・cross-cutting concernsはやっぱりない・・・
CCCの表現するもの
様々な圏がCCCだが、ここで説明するものは単純型付きラムダ計算と直観主義論理。
Curry-Howard-Lambek対応という。
前二者のラムダ計算と直観主義論理との関係だけがよく使われる。
直観主義はあまりなじみがない(特にB4には)と思われるので、単純型付きラムダ計算を元に説明していく。
幸いHaskellやってるんだし、大丈夫・・・だよね?
単純型付きラムダ計算 知ってる人は退屈な、知らない人でもすぐわかる説明。(たぶん)
単純型付きラムダ計算(最小構成)
ラムダ計算+型
xは型付きラムダ式
λx:A.MはMが型付きラムダ式であり、Aが型であるとき(かつそのときに限り)ラムダ式
(M N)はM, Nが型付きラムダ式であるとき(かつそのときに限り)型付きラムダ式
αは型(ground type)
BAはA, Bが型であるとき(かつそのときに限り)型
Haskellなどでいう A -> B
余談:矢印
A→Bと同じ意味を持つ記号はいくつかある。
今回使ったBA
主に関数型言語、ラムダ式で使われた印象がある。
あくまで個人的な意見だが。
一部の型付きラムダ計算ではλxA.Mのような書き方をしていたし、高階関数が面倒なので、使えにくかったのでは?
A⊃B
論理の世界では今も使われている。
A⇒B
どちらかといえば、メタレベルでのimplicationの印象。
余談:なぜ冪乗?
おそらく、要素数が冪乗になるから
予想だが、そう間違ってはいまい。
例)A={0,1,2}、B={0,1}に対しA→Bの数は?
答え:8つ(|B||A|)
012->0
01->0 2->1
02->0 1->1
12->0 0->1
0->0 12->1
1->0 02->1
2->0 01->1
012->1
Aの要素それぞれに対して、Bの要素数分の選択肢。
余談:「かつそのときに限り」
ラムダ式の定義に出現している言葉。
集合を一意に定めるために書く。
この文言がないと、「規則に書かれていない要素」が入りうる。
例えばλx:A.3がラムダ式と扱われる可能性がある。
文言があることで、3はラムダ式ではないので入らない。
通常は、「以下の規則を満たす最小の集合」という定義をする。
面倒だし読みにくくなるし邪魔だし。
単純型付きラムダ計算(少し拡張)
ラムダ式に組とunitを追加
(M, N)はMとNが型付きラムダ式(ry
(fst M)はMが(ry
(snd M)はMが(ry
unitは型付きラムダ式
A×BはAとBが型(ry
Unitは型
余談:型付きラムダ計算というもの
この話からわかるように、型付きラムダ計算と呼ばれるものは山のようにある。
「単純」でさえ、これに和を加えたものなどもある。
CCCは型付きラムダ計算と対応する。よって、 それだけのCCCがある。
構造以外にも、ground typeとしてintだとかboolだとか使えたりする。
型付け規則について
型が付くという関係をΓ┣ M : Aのように書く。
(変数の型)文脈Γにおいて、MはAという型が付く。
型付け規則は、線を引いた上下に関係を書く。
上には「仮定として成り立つ関係」を書く。
下には「結果として成り立つ関係」を書く。
線の横には規則の名前を書く。
下の例だと、「仮定なしに」「文脈ΓでunitがUnitに型付けられ」「その規則をUnitと呼ぶ」ことを表す。
Unit Γ┣ unit : Unit
型付け規則(1/2)
Var Γ┣ x : A
Lam Γ┣ λx:A.M : BA
Γ, x:A┣ M : B
Γ┣ (M N) : B
Γ┣ M : BA Γ┣ N : A App
Unit Γ┣ unit : Unit
x : A ∈ Γ
型付け規則(2/2)
Γ┣ (M, N) : A×B
Γ┣ M : A Γ┣ N : B Prod
Snd
Γ┣ M : A×B
Γ┣ (fst M) : A
Γ┣ (snd M) : B
Fst
Γ┣ M : A×B
余談:き?け?
型付き?型付け?
”typed”が型付き、”typing”が型付けに相当。
型をつけるラムダ計算ではなく型がくっついたラムダ計算。
規則に型がつくわけじゃなくて、型をつける規則。
ちなみに、英語の”type”は動詞。型をつける。
はいはいどうでもいいですねわろすわろす。
単純型付きラムダ計算の性質
必ずβ簡約が終了する。
逆に言えば、β簡約が終了しないラムダ式は型付きラムダ計算として書けない。
型のないラムダ計算も、型推論という形で同じ範囲の項に限定することができる。
型推論できる=型付きラムダ計算で書ける
他は・・・いや、十分か。
余談:式(プログラム)の妥当性
型付けできない型付きラムダ式というのがある。
λx:A.(x x)とか。
前のxが関数型でないため、型付け規則に合致しない。
通常のラムダ式と違い、「妥当(valid)な型付きラムダ式」という概念があることを意味している。
通常のラムダ式に、妥当でないラムダ式はない。
型は妥当であるとか妥当でないという概念を作り、それによってユーザーを助けている。
妥当でないプログラムは正しく動かない(かもしれない)からはじく(rejectする)。
CCCと単純型付きラムダ計算の 対応
ようやく話の本題。ここまでは前座。
CCCと型付きラムダ計算の対応
対象は全て型
項ではないので注意。
射は、環境の型から項の型の導出
f : A → Bは A┣ Bを証明する(ための規則の列の合成射)fを意味する。
・・・え?イミフ?
説明の順序
型付きラムダ計算を少し考え直す。
CCCの要素の定義を見ながら厳密に対応をとる。
とりあえずCCCは置いておくけど
まず型付きラムダ計算のほうから見る。
でも覚えておいてほしいこと。
CCCの対象は型と
CCCの射は型の導出Γ┣ Aと
それぞれ対応する。
というか、対応させる。これから。
型付きラムダ計算を考え直す 都合が悪ければ、本質的に等価な何かに変更すればいい。
型付け規則 Γ┣ BからA┣ Bという形へ
Bは型として、Aも型とすると若干型付け規則の形に合わない。
Aの側は「変数と型のリスト」であり、Bは「項と型」。
リストを一つの型に押し込める必要がある。
リストが駄目ならペアにすればいいじゃない。
cons=組 (積)
nil=unit (1)
文脈の変換
x1 : A1, x2 : A2, ... , xn : An
(...((unit, x1), x2), ...), xn) : (...((Unit×A1)×A2)×...×An)
これで確かに要素が一つになった。
推論の方法がやや変わる(変数の型付けに射影をとる必要あり)が、基本的には同じ。
型付け規則renewal
Var規則とLam規則が変わるのみ。他は全てΓを一つの型にすればよい。(変数の衝突に注意)
Var (L, x) : C×A┣ x : A
Proj (L, y) : C×B┣ x : A
L : C┣ x : A
Lam L : C┣ λx:A.N : BA
(L, x) : C×A┣ N : B
問題:ラムダ式は?
Q:CCCと型付きラムダ計算が対応すると言ったのに、ラムダ式と対応するものがないじゃないか。どういうこと?
A:renewalによって、実は
ラムダ式 ≡ 型の導出 が成立するようになったのだ。
ちなみに、renewal前はラムダ式のほうが細かい指示ができた。
例)λx:A.λy:A.xとλx:A.λy:A.yは導出では区別不可能。
区別できない?
どちらも、Var,Lam,Lamの順。ラムダ式は違う。
┣ λx:A.λy:A.x : (AA)A
x : A┣ λy:A.x : AA
x : A, y : A┣ x : A
Lam
Lam Var
┣ λx:A.λy:A.y : (AA)A
x : A┣ λy:A.y : AA
x : A, y : A┣ y : A
Lam
Lam Var
区別できなかったもの
以前の形式だと、唯一Varのみ「構造以外の何か」で判別する規則になっている。
リストに含まれるかどうか。リスト内の順序など全く関係ない。
他は構造を分解するだけ。Unitは分解する必要すらない。
でもUnitを使う場合必ずunitが値。
Varを使う場合、どの変数であっても(型さえ同じなら)同じように規則が使われる。
Varを使うと、ラムダ式のほうが細かく指定できる。
Var規則の変形
VarとProjに分かれた。
それぞれ「一番近い束縛変数を消去」「一番近い束縛変数が導出対象」という状態。
どちらも構造に依存し、「構造以外の何か」ではなくなっている。
簡単に言えば、VarとProjにより「今の地点から何番目の変数を参照しているか」を表す。
de Bruijn indexみたいな考えだと思えば。
区別できなかった例
上に余計なProjが入ってきている!
区別できた!
unit : Unit┣ λx:A.λy:A.y : (AA)A
(unit, x) : Unit×A┣ λy:A.y : AA
((unit, x), y) : Unit×A×A┣ y : A
Lam
Lam Var
unit : Unit┣ λx:A.λy:A.x : (AA)A
(unit, x) : Unit×A┣ λy:A.x : AA
((unit, x), y) : Unit×A×A┣ x : A
Lam
Lam Proj
(unit, x) : Unit×A┣ x : A Var
つまり
射≡型付け≡ラムダ式+環境、すなわち
射≡ラムダ式+環境 ってこと。
これで解決できたね。
規則と図式の対応 タイトルの割に、圏の構造の定義の話だったりする。
CCCの対象と型の対応
直感的には、Unitは1、組は積、関数は冪と対応
まあ、同じ記号使ったし、ね。
Ground type(αなど)はCCCの中に対応する対象があると仮定
そのようなCCCが対応する、と考える。
例:α×(βUnit)はα×(β1)と対応
まあ見たまんま。Unitが変わるくらい。
圏による終対象・積・冪の定義
規則との対応をとるに当たって必要なので、ここで説明。
圏における構造の定義は、大体次のパターン。
ある射が存在し、可換である図式がある。
積・冪もこれにもれない。
終対象はほとんど図式を使わないけれど、ね。
終対象 終わりがあるなら始まりもある。始対象という。説明しないけど。
終対象(terminal object)
全ての対象Aに対して唯一の射! : A → 1が存在。
どんなAに対しても!が射の名前。不思議だけどよくあること。
A 1 !
終対象とUnit
何かからUnitを導くのは常にUnitの規則を使えばよい。
文脈に関係なくunit : Unitと型付けられる。
あらゆる対象に!を持つことと対応する。
A 1
Unit L : A┣ unit : Unit
!
積 績と間違えられること多数。紡ぐのか、積むのか・・・
積
A×Bはπ1 : A×B → A, π2 : A×B → Bを伴い、 ある対象Cと射f : C → A, g : C → Bについて、 以下の図式を可換にする<f, g> : C → A×Bが 存在するものを言う。
点線の射は、他の部分から一意に導かれる射。この図
ではf, g, π1, π2から導かれる。 C
A×B B A
f g
π1 π2
<f, g>
型付けにおける積(1/2)
組を型付ける場合
さきほどの図のCが環境である。
C → A×Bという射(文脈C下で型A×B)は、 f : C → A(文脈C下で型Aがつく)と g : C → B(文脈C下で型Bがつく)の存在から導ける、と言っている。
型付けにおける積(図1)
C
A×B B A
f g
π1 π2
<f, g>
Prod L : C┣ (M, N) : A×B
L : C┣ M : A L : C┣ N : B
型付けにおける積(2/2)
組を分解する場合
C → A×Bが前提として存在、π1とπ2はやはり存在する。
合成で終了。
π1とπ2はそれぞれfstとsndに対応する。
型付けにおける積(図2)
C
A×B B A
π1○f π2○f
π1 π2
f
Snd L : C┣ M : A×B
L : C┣ (fst M) : A L : C┣ (snd M) : B Fst
L : C┣ M : A×B
余談:双対性(duality)
様々なものをひっくり返した構造(性質etc)。
圏では、非常に簡単なことに、射を逆に向けたもの
以下は余積(coproduct)と呼ばれる構造の持つ図式
co- が双対なものを表す。colimitとかcoconeとか。
例外もある。終対象の双対である始対象はinitial object。
実はこれ、一般に言う和の構造。つまり積の双対は和。
C
A+B B A
f g
ι1 ι2
[f, g]
余談:対象の一意性
実のところ、A×BとB×Aは、対象として完全に同じものである。
それぞれの射影射は全く同じ。
圏では、こういった時に「isomorphicなものを 除いて一意」という言い方をする。
簡単に言えば同等なものは本質的に同じものとする。
冪 和、積、冪とならぶはずなのに、可換じゃない。
冪
BAは、evalBA : BA×A → Bを伴い、ある対象Cとg : C×A → Bについて、以下の図式を可換にするcurry(g) : C → BAが存在するものを言う。
f×gは、f : A → B, g : C → DのときにA×C → B×Dとなる(直感的にはfとgの積である)射。
C×A
B BA×A evalBA
curry(g)×idA g
余談:curryとeval
どちらも関数型言語のそれ。
カリー化は複数の引数をとる関数を、一つずつ引数をとって関数を返す関数に変更するもの。
A×B→CをA→B→C
eval(apply?)は関数と引数をとって適用を行うもの。
(A→B)×A→B
使用した本の表記であって、常にこの名称というわけではない。
型付けにおける冪(1/2)
関数抽象を型付ける場合
やはりCが環境である。
C → BAという射(文脈C下で型BA)は、 g : C×A → B(環境C×A下で型B)の存在から導ける、と言っている。
C×Aは環境にAという型の要素を追加したもの。
型付けにおける冪(図1)
C×A
B BA×A evalBA
curry(g)×idA g
Lam L : C┣ λx:A.N : BA
(L, x) : C×A┣ N : B
型付けにおける冪(2/2)
関数適用を型付ける場合
C → BAとC → Aが前提として存在。
積の定義からC → BA×Aが存在。
evalBA : BA×A → Bと合成することでC → Bが導ける。
型付けにおける冪(図2)
B BA×A evalBA
C┣ (M N) : B
C┣ M : BA C┣ N : A App
C
BA A
f g
<f, g>
evalBA○<f, g>
変数 CCCでは名前なんてない。つまりde Bruijn notationとほぼ等価。
最後に追加
VarとProjと対応する射
簡単に言えば、Varはπ2と、Projはπ1とそれぞれ対応する。
Projには「残りの環境から型付けする」射が必要だが。
C×A A C π1 π2
Var (L, x) : C×A┣ x : A
Projの対応
C×B B C π1 π2
Proj (L, y) : C×B┣ x : A
L : C┣ x : A
A
f f○π1
というわけで
全ての規則と対応する射の存在がわかった。
全体としては
全ての対象は型
全ての射は、ドメインが環境、コドメインがラムダ式(環境下で型が付くラムダ式に限る)の型であり、射そのものは型を付ける規則の列(ラムダ式の構造)
と対応する。
ただ、冪の内容上、頻繁に図式が離れてしまう。
例 きれいな図式を期待?――残念。
実際に圏で型付けを・・・
SKIコンビネータでもやりますか。
簡単?後悔しますよ・・・?
ラムダ式として書くと、それぞれ以下のように。
S = λf:(CB)A.λg:BA.λx:A.((f x) (g x))
((CA)BA)(CB)Aという型がつく。
K = λx:A.λy:B.x
(AB)Aという型がつく。
I = λx:A.x
AAという型がつく。
一番簡単なIから
1 AA
1×A A π2
AA×A
evalAA curry(π2)×idA
curry(π2)
あれ~・・・?
なんか変な感じがするなあ・・・
図式が離れてるせいか?
じゃあ次、K
1 (AB)A
(1×A)×B A π1 1×A
π2
AB×B
evalBA curry(π1○π2)×idA
1×A AB curry(π1○π2)
(AB)A×A
evalAAB
もう名前書くのいや・・・
なんなのcurry(curry(π1○π2))って?
ちなみに最終的な射。
Sは図だけにする。
地獄のS(1/2) 関数適用部分
1×(CB)A×BA×A C
1×(CB)A×BA
A
BA BA×A
B
1×(CB)A
(CB)A
(CB)A×A
CB
CB×B
地獄のS(2/2) 関数抽象部分
1 ((CA)BA)(CB)A
1×(CB)A
((CA)BA)(CB)A×(CB)A
(CA)BA 1×(CB)A×BA
(CA)BA×BA
CA
1×(CB)A×BA×A C
CA×A
ははは・・・
もういやです。
かんべんしてください。
と言いたくなるような内容になるのでした。
以上! 眠かったかな?