Кодировка Изоморфизмами
Специально для канала Формальная Философия. В статье не рассматривается ничего сложнее чем encode-decode XML <-> JSON вместе с правилами enc dec = id и dec enc = id. Просто вместо переменных XML/JSON мы рассматриваем несколько конкретных типов: Iso, Equiv, Pi, Interval. Другими словами, мы будем кодировать эти типы и транспорты между ними с помощью изоморфизмов.
Вступление
Как вы знаете все индуктивные типы (Nat, List и т.д.) могут быть представлены в Чёрч-кодировке которая есть упаковка категорной модели. Идея кодирования следующая, раз мы работаем в теории категорий, то всё сущности мы рассматриваем как объекты и связи между ними (везде одно и то же). Значит и индуктивные типы мы раскладываем в это же, т.е. другими словами мы строим категорную модель конкретных индуктивных инстансов data, или как говорят мы строим категории F-алгебр:
natOb: U
= (X: U)
* (zero: X)
* (succ: X -> X)
* unit
natHom (x1 x2: natOb): U
= (map: x1.1 -> x2.1)
* (mapZero: Path x2.1 (map (x1.2.1)) (x2.2.1))
* (mapSucc: (x: x1.1) -> Path x2.1 (map (x1.2.2.1 x)) (x2.2.2.1 (map x)))
* unit
isHomotopyInitialNat (I: natOb): U
= (C: natOb)
* (x: natHom I C)
* ((y: natHom I C) -> Path (natHom I C) x y)
Здесь у нас unit-терминейтед record — это что-то типа null-терминейтед стринг. Я думаю идея понятна. Если взять лимит в этой категории получится штука изоморфная черч-кодировке. В HoTT это можно увидеть в параграфе Nat-алгебр (5.4.1) и Nat-гомоморфизмов (5.4.2). Полная кодировка должна включать не только зависимый элиминатор, через который уже выражается рекурсор и кейс анализ, но и правила связывающие фьюжин intro-elim двумя правилами beta и eta которые называются ещё ректракт и секция (enc dec = id, dec enc = id).
Постановка задачи
В этой статье мы покажем, что, во-первых, интернализация MLTT в сущности есть кодировкой изоморфизмами, и, во-вторых, покажем, что в кодировке Пи у нас функция равна своей аппликации (да да, вам не послышалось).
Ну, я думаю не нужно доказывать или показывать, что всё в MLTT есть тип,
или всё в мире есть феномен. А раз так, то мы можем построить категорию
этих объектов и морфизмов между ними (это у математиков что-то типа если
есть гора, то на неё нужно взобраться, или как я называю в любой
непонятной ситуации строй категорию). Т.е. тоеретически возможно засунуть
любой тип в сигнатуру изоморфизма:
isIso (XML JSON: U): U -- form
= (f: XML -> JSON) -- intro
* (g: JSON -> XML) -- elim
* (s: section XML JSON f g) -- beta
* ( retract XML JSON f g) -- eta
section (A B:U) (f:A->B) (g:B->A): U
= (b:B) -> Path B (f(g(b))) b
retract (A B:U) (f:A->B) (g:B->A): U
= (a:A) -> Path A (g(f(a))) a
Давайте попробуем это сделать для сложных типов, чтобы доказать первую часть задачи, а потом попробуем закодировать самый базовый Пи-тип на котором строится вся теория MLTT, и докажем вторую часть задания.
Аксиома Унивалентности
Она не золотой грааль, а обычнейший тип, а значит для нее существует 5 правил теории типов Мартина-Лёфа. В сущности, чтобы не лукавить, надо сказать, что унивалентность вшивается в тайпчекеры обычно. В cubicaltt intro-правило типовой унивалентности называется Glue.
univ_Formation (A B: U)
: U = equiv A B -> Path U A B -- Formation
equivToPath (A B: U)
: univ_Formation A B -- Intro
pathToEquiv (A B: U) (p: Path U A B)
: equiv A B -- Elim
eqToEq (A B : U) (p : Path U A B)
: Path (Path U A B) (equivToPath A B
(pathToEquiv A B p)) p -- Beta
idToPath (A B: U) (w: equiv A B)
: Path (equiv A B) (pathToEquiv A B
(equivToPath A B w)) w -- Eta
Превращаем аксиому унивалентности в енкодер-декодер или изоморфизм:
UnivType (A B: U): isIso (equiv A B) (Path U A B)
= (equivToPath A B, pathToEquiv A B,eqToEq A B,idToPath A B)
Неплохо, поехали дальше.
Изоморфизм Изоморфизма
Так как фибрационная эквивалентность есть одним из видов равенства и аксиома унивелентности это в сущности часть конструкции которая связывает фьюжин типов фибрационной эквивалентности Equiv и типа Path встроенного в кубический тайпчекер, то можно построить точно такой же изоморфизм между Path и типом Iso, который так же является одним из видов равенства, простой и поняный всем программистам encode/decode.
iso_Form (A B: U)
: U = isIso A B -> Path U A B
iso_Intro (A B: U)
: iso_Form A B
iso_Elim (A B: U)
: Path U A B -> isIso A B
iso_Comp (A B : U) (p : Path U A B)
: Path (Path U A B) (iso_Intro A B (iso_Elim A B p)) p
iso_Uniq (A B : U) (p: isIso A B)
: Path (isIso A B) (iso_Elim A B (iso_Intro A B p)) p
Заметьте как мы циклически встраиваемся в нижний уровень подсвовывая в сами изоморфизм сигнатуру с изоморфизмом
IsoPathType (A B: U): isIso (isIso A B) (Path U A B)
= (iso_Intro A B,iso_Elim A B,iso_Comp A B,iso_Uniq A B)
Пи-тип
Теперь давайте перейдем к самому простому и самому загадночному изоморфизму изоморфизму Пи-типов. Вспомним категорные догматы: чтобы закодировать что-то, нам надо построить категорию эндоморфизмов этого. В нашем случае, так как мы отложили учебник по теоркату и вышиваем на XML <-> JSON сигнатурах, мы просто строим изоморфизм между Пи и Пи:
PiType (A: U) (B: A -> U): isIso (Pi A B) (Pi A B)
= (app A B,lam A B,Pi_Beta A B,Pi_Eta A B)
Pi (A: U) (B: A -> U)
: U = (x:A) -> B(x)
lam (A: U) (B: A -> U) (b: Pi A B)
: Pi A B = \(x: A) -> b x
app (A: U) (B: A -> U) (f: Pi A B) (a: A)
: B a = f a
Pi_Beta (A: U) (B: A -> U) (f: Pi A B)
: Path (Pi A B) (\(x:A) -> f x) f
Pi_Eta (A: U) (B: A -> U) (f: Pi A B)
: Path (Pi A B) f (\(x:A) -> f x)
Как видно мы тут немного подхачили, но вполне законно так как мы просто перенесли a : A из телескопа параметров в правую часть результата и протащили в путь: Path (B a) -> Path (Pi A B).
beta_classic (A:U)(B:A->U)(f: Pi A B) (a:A) : Path (B a) ((lambda A B f) a) (f a)
Pi_Beta (A:U)(B:A->U)(f: Pi A B) : Path (Pi A B) (\(x:A) -> f x) f
Теперь докажем вторую задачу и главный номер представления (то, что функция равна своей аппликации, ну бредятина же на звук!):
PiType (A: U) (B: A -> U): isIso (Pi A B) (Pi A B)
= (lam A B,lam A B,Pi_Beta A B,Pi_Eta A B)
= (app A B,lam A B,Pi_Beta A B,Pi_Eta A B)
= (app A B,app A B,Pi_Eta A B,Pi_Eta A B)
Любой из этих вариантов чекается!
Checking: PiType
File loaded.
>
На самом деле, такие тавтологии рождаются потому, что мы в isIso поставили равные типы, до этого в примерах мы делали транспорты из крайних точек пути, тут же у нас петля. Но, тем не менее, этот терм PiType в разных инкарнация вы можете встретить в любой статье про лямбда исчисление, так как он является вычислительной семантикой зависимого лямбда исчисления (для примера классическая запись в виде терма PTS):
PTS (A: U): U
= (Pi_Former: (A -> U) -> U)
* (Pi_Intro: (B: A -> U) -> Pi A B -> Pi A B)
* (Pi_Elim: (B: A -> U) -> Pi A B -> Pi A B)
* (Pi_Comp1: (B: A -> U) (a: A) (f: Pi A B)
-> Equ (B a) (Pi_Elim B (Pi_Intro B f) a) (f a))
* (Pi_Comp2: (B: A -> U) (a: A) (f: Pi A B)
-> Equ (Pi A B) f (\(x:A) -> f x)) * unit
Функциональная экстенсиональность
В качестве бонус трека, funext. В этом примере мы показываем, что терм funext — это все-лишь интро правило, формация же есть классические изоморфизм двух функций f и g, которые находятся в пространстве функций A -> B (в этом примере не зависимых, зависимые вам на домашку).
funext_form (A B: U) (f g: A -> B)
: U = Path (A -> B) f g
funext (A B: U) (f g: A -> B) (p: (x:A) -> Path B (f x) (g x))
: funext_form A B f g
happly (A B: U) (f g: A -> B) (p: funext_form A B f g) (x: A)
: Path B (f x) (g x)
funext_Beta (A B: U) (f g: A -> B) (p: (x:A) -> Path B (f x) (g x))
: (x:A) -> Path B (f x) (g x)
funext_Eta (A B: U) (f g: A -> B) (p: Path (A -> B) f g)
: Path (Path (A -> B) f g) (funext A B f g (happly A B f g p)) p
Еще в качестве упражнения функциональную экстенсиональность можно выразить через интервал.
data I = i0 | i1 | seg <i> [(i=0) -> i0, (i=1) -> i1]
В статье только сигнатуры, все пруфы в https://github.com/groupoid/cubical.