Лямбда кодування
НАМО ЛЯМБДАГАРБХА
Так, як ви всі знаєте, в PTS системах для кодування індуктивних типів даних прийнято використовувати лямбда числення. Всі ви знаєте про кодування Church, Parigot, Scott. Якщо хто забув, я швидко нагадаю! Але є одне особливе кодування, яке краще ніж усі ці кодування, але яке чомусь зовсім не описане в літературі, але описане було Майя Віктором (Бразилія). Його ж синтаксис Caramel використовувався для демонстрації всіх кодувань у нетипизованому лямбда-численні.
Будемо використовувати інфіксну дужкову форму запису лямбди: "(a b -> c)" означає "λ a b . c", щоб λ не дратувала очі і легше було розпізнати візерунок послідовності.
Church кодування
Сама проста. Вона зображає структуру у вигляді своїх правих фолдів. Будемо розглядати найпростіші структури даних, натуральні числа. Вони ж використовуються для чистого моделювання всесвітів тайпчекера.
0 = (f x -> x)
1 = (f x -> (f x))
2 = (f x -> (f (f x)))
3 = (f x -> (f (f (f x))))
pred = (n (g h -> (h (g succ))) (const zero) id) — O(n), погано.
foldNat = (s z nat -> (nat s z)) — Нерекурсивний, добре.
З нею є "маленька" проблема: pred такого числа або tail списку займає O(n).
Scott кодування
Дана Скотт запропонував кодувати структури не у вигляді їх правих фолдів, а у вигляді патерн-мачінг елімінаторів.
0 = (f x -> x)
1 = (f x -> (f (f x -> x)))
2 = (f x -> (f (f x -> (f (f x -> x)))))
3 = (f x -> (f (f x -> (f (f x -> (f (f x -> x)))))))
pred = (nat -> (nat (pred -> pred) _)) — O(1), добре.
foldNat = (s z nat -> (nat (pred -> (s (foldNat s z pred))) z)) — Рекурсія, погано.
Для такого кодування у нас pred натурального числа займає константний час, але для його вираження потрібна рекурсія. А ми це в Групоїд Інфініті ніяк не можемо дозволити.
Кодування Parigot
Є сумішшю Скотт і Чорч кодувань. Парігот елімінує рекурсію з фолдів.
0 = (f x -> x)
1 = (f x -> (f (f x -> (f x)) (f x -> x)))
2 = (f x -> (f (f x -> (f (f x))) (f x -> (f (f x -> (f x)) (f x -> x)))))
3 = (f x -> (f (f x -> (f (f (f x)))) (f x -> (f (f x -> (f (f x))) (f x -> (f (f x -> (f x)) (f x -> x)))))))
pred = (nat -> (nat (_ pred -> pred) _)) -- O(1), добре.
foldNat = (nat -> (nat (fold _ -> fold) _)) -- нерекурсивно, добре.
Як ви бачите Парігот начебто OK, але терми ростуть як на дріжджах, себто експоненційно. Однак, ми можемо позбутися цього!
Кодування CPS
Замість нумералів Чорча ми зберігаємо континуатори (c -> (c f x)). Опис цього кодування ви не знайдете в інтернеті в жодному пейпері.
0 = (f x -> x)
1 = (f x -> (f (c -> (c f x)) (f x -> x)))
2 = (f x -> (f (c -> (c f x)) (f x -> (f (c -> (c f x)) (f x -> x)))))
3 = (f x -> (f (c -> (c f x)) (f x -> (f (c -> (c f x)) (f x -> (f (c -> (c f x)) (f x -> x)))))))
pred = (nat -> (nat (_ pred -> pred) _)) — O(1), добре!
foldNat = (s z nat -> (nat (cont pred -> (s (cont pred))) z)) — нерекурсивна, добре!
Терми всі ростуть так як при Чорчі просто множаться на константу - розмір континуатора. Одна проблема, шо для цього кодування потрібні Self-типи Пенг-Фу і Ко, слабка форма рекурсивних типів. Але індукція і все інше виходить безкоштовно для цього кодування.
Бенчмарк CPS кодування прот Church кодування:
Pack/Unpack 1 000 000 Inductive Nat Church: {410682,#Fun}
Pack/Unpack 1 000 000 Inductive Nat CPS: {712684,1000000}
Pack/Unpack 1 000 000 Inductive Church List: {717695,'_'}
В два рази повільніше (всього).
Church
CPS
ОМ ЛЯМБДА ГАРБХА ХУМ