Який комбінаторний логічний еквівалент інтуїціоністської теорії типу?


87

Нещодавно я закінчив університетський курс, де проходили Haskell та Agda (залежний тип функціональної мови програмування), і мені було цікаво, чи можна замінити лямбда-числення в них комбінаційною логікою. З Haskell це здається можливим за допомогою комбінаторів S і K, що робить його безточковим. Мені було цікаво, що це еквівалент для Агди. Тобто, чи можна зробити залежну від типу функціональну мову програмування еквівалентною Agda, не використовуючи жодних змінних?

Крім того, чи можна якось замінити кількісне визначення комбінаторами? Я не знаю, чи це збіг, але універсальна кількісна оцінка, наприклад, робить підпис типу схожим на лямбда-вираз. Чи є спосіб видалити універсальну кількісну оцінку з підпису типу, не змінюючи його значення? Наприклад, у:

forall a : Int -> a < 0 -> a + a < a

Чи можна те саме висловити без використання загальної думки?


21
Почніть із з’ясування найбільш залежних типів для K (легкий) та S (досить волохатий). Було б цікаво ввести константи для Set та Pi, а потім спробувати реконструювати основну (невідповідну) систему Set: Set. Думаю далі, але мені треба зловити літак.
свинарник

Відповіді:


52

Тож я трохи більше подумав про це і домігся певного прогресу. Ось перший удар у кодуванні чудово простої (але суперечливої) Set : Setсистеми Мартіна-Лефа в поєднанному стилі. Це не найкращий спосіб закінчити, але це найпростіше місце для початку. Синтаксис цієї теорії типів - це просто лямбда-числення з анотаціями типів, Pi-типами та набором всесвіту.

Теорія типу цілей

Для повноти викладу правила. Валідність контексту просто говорить, що ви можете будувати контексти з порожнього, приєднуючись до нових змінних, що населяють Sets.

                     G |- valid   G |- S : Set
--------------     ----------------------------- x fresh for G
  . |- valid         G, x:S |- valid

І тепер ми можемо сказати, як синтезувати типи для термінів у будь-якому даному контексті, і як змінити тип чогось аж до обчислювальної поведінки термінів, які він містить.

  G |- valid             G |- S : Set   G |- T : Pi S \ x:S -> Set
------------------     ---------------------------------------------
  G |- Set : Set         G |- Pi S T : Set

  G |- S : Set   G, x:S |- t : T x         G |- f : Pi S T   G |- s : S
------------------------------------     --------------------------------
  G |- \ x:S -> t : Pi S T                 G |- f s : T s

  G |- valid                  G |- s : S   G |- T : Set
-------------- x:S in G     ----------------------------- S ={beta} T
  G |- x : S                  G |- s : T

У невеликих варіаціях від оригіналу я зробив лямбду єдиним оператором прив'язки, тому другим аргументом Pi має бути функція, обчислююча спосіб, яким тип повернення залежить від вводу. За домовленістю (наприклад, в Agda, але, на жаль, не в Haskell), область лямбди поширюється вправо, наскільки це можливо, тому ви часто можете залишати абстракції без фігурних дужок, коли вони є останнім аргументом оператора вищого порядку: ви бачите, що я зробив що з Пі. Ваш тип Agda (x : S) -> Tстає Pi S \ x:S -> T.

( Відступ . Анотації типу на лямбда необхідні, якщо ви хочете мати можливість синтезувати тип абстракцій. Якщо ви перейдете до типу перевірку як ваш режим роботи, вам все одно потрібні анотації для перевірки бета-редексу, як (\ x -> t) s, оскільки у вас немає можливості щоб вгадати типи деталей із цілого. Раджу сучасним дизайнерам перевірити типи та виключити бета-редекси з самого синтаксису.)

( Відступ . Ця система є непослідовною, оскільки Set:Setдозволяє кодувати різноманітні "парадокси брехунів". Коли Мартін-Леф запропонував цю теорію, Жирар надіслав йому її кодування у своїй власній невідповідній системі U. Подальший парадокс, обумовлений Гуркенсом, найоптимальніша токсична конструкція, яку ми знаємо.)

Синтаксис і нормалізація комбінатора

У будь-якому випадку, у нас є два додаткові символи, Pi та Set, тому, можливо, нам вдасться поєднати переклад із S, K та двома додатковими символами: я вибрав U для всесвіту та P для продукту.

Тепер ми можемо визначити нетипізований комбінаторний синтаксис (із вільними змінними):

data SKUP = S | K | U | P deriving (Show, Eq)

data Unty a
  = C SKUP
  | Unty a :. Unty a
  | V a
  deriving (Functor, Eq)
infixl 4 :.

Зверніть увагу, що я включив засоби для включення вільних змінних, представлених типом, aу цей синтаксис. Окрім того, що він є рефлексом з мого боку (кожен синтаксис, гідний цього імені, є вільною монадою із returnвбудовуванням змінних та >>=виконанням заміни), він буде зручним для представлення проміжних етапів у процесі перетворення термінів із прив'язкою до їх комбінаторної форми.

Ось нормалізація:

norm :: Unty a -> Unty a
norm (f :. a)  = norm f $. a
norm c         = c

($.) :: Unty a -> Unty a -> Unty a        -- requires first arg in normal form
C S :. f :. a $. g  = f $. g $. (a :. g)  -- S f a g = f g (a g)   share environment
C K :. a $. g       = a                   -- K a g = a             drop environment
n $. g              = n :. norm g         -- guarantees output in normal form
infixl 4 $.

(Вправа для читача полягає у визначенні типу для точно нормальних форм та загостренні типів цих операцій.)

Представляючи теорію типів

Тепер ми можемо визначити синтаксис для нашої теорії типів.

data Tm a
  = Var a
  | Lam (Tm a) (Tm (Su a))    -- Lam is the only place where binding happens
  | Tm a :$ Tm a
  | Pi (Tm a) (Tm a)          -- the second arg of Pi is a function computing a Set
  | Set
  deriving (Show, Functor)
infixl 4 :$

data Ze
magic :: Ze -> a
magic x = x `seq` error "Tragic!"

data Su a = Ze | Su a deriving (Show, Functor, Eq)

Я використовую подання індексу де Брюйна в манері Бельгард і Хука (як популяризували Берд і Патерсон). Тип Su aмає на один елемент більше a, ніж , і ми використовуємо його як тип вільних змінних під підшивкою, причому Zeяк нещодавно зв’язану змінну і Su xє зміщеним поданням старої вільної змінної x.

Переклад термінів до комбінаторів

І закінчивши це, ми отримуємо звичайний переклад, заснований на абстракції дужок .

tm :: Tm a -> Unty a
tm (Var a)    = V a
tm (Lam _ b)  = bra (tm b)
tm (f :$ a)   = tm f :. tm a
tm (Pi a b)   = C P :. tm a :. tm b
tm Set        = C U

bra :: Unty (Su a) -> Unty a               -- binds a variable, building a function
bra (V Ze)      = C S :. C K :. C K        -- the variable itself yields the identity
bra (V (Su x))  = C K :. V x               -- free variables become constants
bra (C c)       = C K :. C c               -- combinators become constant
bra (f :. a)    = C S :. bra f :. bra a    -- S is exactly lifted application

Набір комбінаторів

Переклад показує, як ми використовуємо комбінатори, що дає нам достатню підказку про те, якими мають бути їх типи. Uі Pє просто конструкторами наборів, тому, пишучи неперекладені типи та дозволяючи "позначення Agda" для Pi, ми повинні мати

U : Set
P : (A : Set) -> (B : (a : A) -> Set) -> Set

KКомбінатор використовується , щоб підняти значення деякого типу Aз постійними функціями в протягом деякого іншого типу G.

  G : Set   A : Set
-------------------------------
  K : (a : A) -> (g : G) -> A

SКомбінатор використовується для ліфтових застосувань по типу, при якому всі частини можуть залежати.

  G : Set
  A : (g : G) -> Set
  B : (g : G) -> (a : A g) -> Set
----------------------------------------------------
  S : (f : (g : G) ->    (a : A g) -> B g a   ) ->
      (a : (g : G) ->    A g                  ) ->
           (g : G) ->    B g (a g)

Якщо ви подивитесь на тип S, ви побачите, що він точно визначає контекстуалізоване правило програми теорії типів, тому саме це робить його придатним для відображення конструкції програми. Це його робота!

Тоді ми маємо заявку лише на закриті речі

  f : Pi A B
  a : A
--------------
  f a : B a

Але є загвоздка. Я писав типи комбінаторів у звичайній теорії типів, а не в комбінаторній теорії типів. На щастя, у мене є машина, яка зробить переклад.

Комбінована система типів

---------
  U : U

---------------------------------------------------------
  P : PU(S(S(KP)(S(S(KP)(SKK))(S(KK)(KU))))(S(KK)(KU)))

  G : U
  A : U
-----------------------------------------
  K : P[A](S(S(KP)(K[G]))(S(KK)(K[A])))

  G : U
  A : P[G](KU)
  B : P[G](S(S(KP)(S(K[A])(SKK)))(S(KK)(KU)))
--------------------------------------------------------------------------------------
  S : P(P[G](S(S(KP)(S(K[A])(SKK)))(S(S(KS)(S(S(KS)(S(KK)(K[B])))(S(KK)(SKK))))
      (S(S(KS)(KK))(KK)))))(S(S(KP)(S(S(KP)(K[G]))(S(S(KS)(S(KK)(K[A])))
      (S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KP)))(S(KK)(K[G]))))
      (S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(S(KS)(S(KK)(KS)))
      (S(S(KS)(S(KK)(KK)))(S(KK)(K[B])))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))
      (S(KK)(KK))))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(S(KS)(S(KK)(KK)))
      (S(S(KS)(KK))(KK)))))(S(S(KS)(S(S(KS)(S(KK)(KS)))(S(KK)(KK))))(S(KK)(KK)))))))

  M : A   B : U
----------------- A ={norm} B
  M : B

Отож, у вас у всій нечитабельній красі: комбінаційна презентація Set:Set!

Існує ще трохи проблем. Синтаксис системи не дає ніякої можливості вгадати G, Aа Bпараметри Sі аналогічно для K, тільки від умов. Відповідно, ми можемо перевіряти алгоритмічно введення деривацій , але ми не можемо просто перевірити терміни комбінатора, як це можна було зробити з оригінальною системою. Що може спрацювати, це вимагати вхідних даних для перевірки друку, щоб містити анотації типу щодо використання S і K, ефективно записуючи виведення. Але це ще одна банка глистів ...

Це гарне місце для зупинки, якщо ви хотіли почати. Решта - "закулісні" речі.

Генерування типів комбінаторів

Я сформував ці комбінаторні типи, використовуючи переклад абстракції дужок із відповідних термінів теорії типів. Щоб показати, як я це зробив, і зробити цей пост не зовсім безглуздим, дозвольте мені запропонувати своє обладнання.

Я можу написати типи комбінаторів, повністю абстрагованих над їх параметрами, наступним чином. Я використовую свою зручну pilфункцію, яка поєднує Pi та лямбда, щоб уникнути повторення типу домену, і досить корисно дозволяє мені використовувати функціональний простір Хаскелла для прив'язки змінних. Можливо, ви майже можете прочитати наступне!

pTy :: Tm a
pTy = fmap magic $
  pil Set $ \ _A -> pil (pil _A $ \ _ -> Set) $ \ _B -> Set

kTy :: Tm a
kTy = fmap magic $
  pil Set $ \ _G -> pil Set $ \ _A -> pil _A $ \ a -> pil _G $ \ g -> _A

sTy :: Tm a
sTy = fmap magic $
  pil Set $ \ _G ->
  pil (pil _G $ \ g -> Set) $ \ _A ->
  pil (pil _G $ \ g -> pil (_A :$ g) $ \ _ -> Set) $ \ _B ->
  pil (pil _G $ \ g -> pil (_A :$ g) $ \ a -> _B :$ g :$ a) $ \ f ->
  pil (pil _G $ \ g -> _A :$ g) $ \ a ->
  pil _G $ \ g -> _B :$ g :$ (a :$ g)

Визначивши їх, я витягнув відповідні відкриті субтерми та провів їх через переклад.

Набір інструментів кодування de Bruijn

Ось як будувати pil. По-перше, я визначаю клас Finнаборів ite, що використовується для змінних. Кожен такий набір має embредагування, що зберігає конструктор, у набір вище, а також новий topелемент, і ви можете їх розрізнити: embdфункція повідомляє, чи є значення на зображенні emb.

class Fin x where
  top :: Su x
  emb :: x -> Su x
  embd :: Su x -> Maybe x

Звичайно, ми можемо створити екземпляр Finдля ZeіSuc

instance Fin Ze where
  top = Ze              -- Ze is the only, so the highest
  emb = magic
  embd _ = Nothing      -- there was nothing to embed

instance Fin x => Fin (Su x) where
  top = Su top          -- the highest is one higher
  emb Ze     = Ze            -- emb preserves Ze
  emb (Su x) = Su (emb x)    -- and Su
  embd Ze      = Just Ze           -- Ze is definitely embedded
  embd (Su x)  = fmap Su (embd x)  -- otherwise, wait and see

Тепер я можу визначити менше або рівне, з операцією послаблення .

class (Fin x, Fin y) => Le x y where
  wk :: x -> y

wkФункція повинна вставляти елементи в xякості найбільших елементів y, так що додаткові речі в yменше, і , отже , в де термінів індексу Брейн, пов'язаний більш локально.

instance Fin y => Le Ze y where
  wk = magic    -- nothing to embed

instance Le x y => Le (Su x) (Su y) where
  wk x = case embd x of
    Nothing  -> top          -- top maps to top
    Just y   -> emb (wk y)   -- embedded gets weakened and embedded

І як тільки ви з цим розібралися, трохи рангового набору черепів робить все інше.

lam :: forall x. Tm x -> ((forall y. Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
lam s f = Lam s (f (Var (wk (Ze :: Su x))))
pil :: forall x. Tm x -> ((forall y . Le (Su x) y => Tm y) -> Tm (Su x)) -> Tm x
pil s f = Pi s (lam s f)

Функція вищого порядку не просто дає вам термін, що представляє змінну, вона дає вам перевантажену річ, яка стає правильним поданням змінної в будь-якій області, де змінна видна. Тобто той факт, що я намагаюся розрізнити різні сфери застосування за типом, дає Haskell достатньо інформації для обчислення зміщення, необхідного для перекладу, до подання де Бруйна. Навіщо тримати собаку і гавкати самому?


це може бути надзвичайно безглуздо, але як це зображення змінюється, якщо додати Fкомбінатор? Fдіє по-різному залежно від свого першого аргументу: Якщо Aє атомом, Mа Nє термінами і PQє складеним, то FAMN -> Mі F(PQ)MN -> NPQ. Це неможливо представити в SK(I)числення, але Kможе бути представлене як FF. Чи можна цим продовжити безточковий MLTT?
kram1032

Я майже впевнений, що існує проблема з цією процедурою абстрагування дужок, зокрема, частина “комбінатори стають постійними”, яка перекладає λx.c в Kc для комбінаторів c ∈ {S, K, U, P}. Проблема полягає в тому, що ці комбінатори є поліморфними і можуть використовуватися для типу, який залежить від x; такий тип не може бути збережений цим перекладом. Як конкретний приклад, перекладено термін λ (A : Set) → λ (a : A) → atype , який не може бути використаний у типі, де тип другого аргументу залежить від першого аргументу. (A : Set) → (a : A) → AS(S(KS)(KK))(KK)
Андерс Касеорг,

8

Я думаю, "Абстракція дужок" за певних обставин також працює для залежних типів. У розділі 5 наступної статті ви знайдете деякі типи K та S:

Епатажні, але значущі випадковості,
залежні від безпечного типу синтаксису та оцінки
Конор Макбрайд, Університет Стратклайда, 2010

Перетворення лямбда-виразу в комбінаторний вираз приблизно відповідає перетворенню природного дедукційного доказу в доказ у стилі Гільберта.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.