thedeemon: (Default)
[personal profile] thedeemon
Для многих моих френдов описанное в этом посте - детский сад, поэтому они могут смело пропустить текст и просто полюбоваться фотографией типизированной лямбды в конце. Я же с данной темой познакомился недавно, мне было интересно, возможно кому-то еще будет тоже. Недавно я писал про логику высказываний и ее параллели в программировании. Но там связи этих двух областей были неформальные, на уровне аналогий. Оказывается, до их формализации был всего один шаг.

Пусть мы оперируем некоторыми высказываниями A,B,C и т.д. Пусть у нас есть доказательство a достоверности высказывания А. Этот факт будем обозначать как a : А.


Пусть теперь у нас есть два высказывания вместе с их доказательствами: a : A и b : B. Тогда мы можем вывести достоверность высказывания A & B, его доказательством будет пара (a,b). Т.е. (a,b) : A & B. Это правило ввода конъюнкции.

Обратным для этого правила будет правило избавления от конъюнкции. Если у нас есть высказывание A & B и его доказательство p, то мы можем вывести достоверность высказывания А и высказывания В. Их доказательствами будут fst p и snd p, т.е. fst p : A и snd p : B. Кроме того, можно описать правило упрощения такого доказательства: fst (a,b) --> a и snd (a,b) --> b. Т.е. более сложное доказательство можно заменить более простым.

Если мы из высказывания А с его доказательством a можем каким-то образом (следуя правилам) получить высказывание В с его доказательством b, то из этого факта мы можем вывести достоверность высказывания A => B. Его доказательство запишем как λa.b или (λa:А).b. Здесь из произвольного доказательства a высказывания А строится доказательство b высказывания В. А вся конструкция доказывает A => B. Это правило ввода импликации. Популярна нотация, где исходные факты записываются над горизонтальной чертой, а то, что из них выводится, - под чертой. Запись данного правила в такой нотации можно внезапно обнаружить на юзерпике [livejournal.com profile] deni_ok:

Первая строчка в квадратных скобках, потому что это допущение, а не утверждение. Для достоверности импликации достаточно умения построить доказательство В из доказательства А, достоверность самого А не требуется.

Обратное правило - избавление от импликации. Если у нас есть доказательство f высказывания A => B и доказательство a высказывания А, то из этого можно вывести достоверность высказывания В, его доказательством будет применение нашего умения строить из А В. Запишем это применение как f a. Оно дает доказательство В, поэтому (f a) : B. Для него есть правило упрощения:
(λx.e) a --> e[a/x]
Если f имеет форму λx.e, то (f a) можно заменить на e, где вместо каждого свободного вхождения x (т.е. не связанного лямбдой внутри e) стоит a.

Дальше, если у нас есть доказательство a высказывания А или доказательство b высказывания B, то мы можем вывести достоверность высказывания A | B. Его доказательством будет пара из имеющегося у нас доказательства и тэга, говорящего, которое из этих двух доказательств у нас есть. Обозначим их как Left a и Right b. Это правило ввода дизъюнкции.

Правило избавления от дизъюнкции: если у нас есть доказательство р высказывания A | B, а также доказательства f : A=>C и g : B=>C, то из них мы можем вывести достоверность С. Его доказательство запишем как cases p f g. Его смысл в том, чтобы посмотреть которое из двух высказываний (А или В) имеется в р и применить одну из импликаций. Для такого доказательства тоже есть правило упрощения:
cases (Left a) f g --> f a
cases (Right b) f g --> g b

В систему также вводится высказвание ⊥, означающее противоречие, абсурд. Для него нет правила ввода, т.к. мы не можем (и не должны мочь) конструктивно его доказать. Зато есть правило избавления: если вдруг каким-то образом у нас появилось доказательство р : ⊥, то вся наша система должна порушиться, а из доказательства абсурда мы можем вывести все, что угодно. Записывается это как (abortA p) : A.

Отрицание определяется следующим образом: -А = А => ⊥.

Что все это дает? Возможность конструктивно строить доказательства разных утверждений.

Пример 1. Эквивалентность (A & B) => C и A => (B => C).
Высказвания эквивалентны, когда следуют друг из друга. Сперва докажем в одну сторону:
((A & B) => C) => (A => (B => C))
Самая внешняя связка - импликация, поэтому доказательством будет лямбда: λf.e, где
f : (A & B) => C и e : A => (B => C).
По правилу избавления от импликации, если (a,b) : A & B, то (f (a,b)) : C.
Тогда ((λa:A) . (λb:B). f (a,b)) : A => (B => C), а значит
(λf : (A & B) => C) . (λa:A) . (λb:B). f (a,b)
и есть искомое доказательство.

Теперь в обратную сторону:
(A => (B => C)) => ((A & B) => C)
Опять лямбда λf.e, где
f : A => (B => C)
e : (A & B) => C

Если a:A и b:B, то (f a) : B => C и ((f a) b) : C.
Значит, e = (λp: A & B). (f (fst p)) (snd p), и искомое доказательство:
(λf : A => (B => C)) . (λp: A & B). (f (fst p)) (snd p)

Пример 2. A => --A.
По определению отрицания -A = A => ⊥, а значит --A = (A => ⊥) => ⊥.
Нам нужно построить доказательство вида (λa:A) . f, где f : (A => ⊥) => ⊥.
Если n : A => ⊥ и а : А, то (n a) : ⊥.
Тогда f = (λn : A => ⊥) . n a, и все доказательство:
(λa:A) . (λn : A => ⊥) . n a

Можно ли доказать обратное (--A => A)? Для этого нам потребуется построить лямбду, которая бы из доказательства f : (A => ⊥) => ⊥ строила бы доказательство а : А. Чтобы применить f, нужно конструктивное доказательство A => ⊥, но т.к. у нас нет правила ввода ⊥, его взять неоткуда. Поэтому доказать исходное высказывание не получится. А значит, логика здесь получилась не классическая (двойное отрицание не означает утверждения), и совсем не булева.

Пример 3.
В предыдущем посте на эту тему говорилось об эквивалентности A=>B и -(A & -B). В классической логике они эквивалентны, а можно ли доказать их эквивалентность в данной системе? Сперва в одну сторону:
(A=>B) => -(A & -B)
т.е. (A=>B) => ((A & (B=>⊥)) => ⊥)
Опять доказательством будет λf.e, где
f : A=>B
e : (A & (B=>⊥)) => ⊥

Заметим, что если a : A и n : B=>⊥, то (f a) : B и (n (f a)) : ⊥.
Тогда e = (λp : A & (B=>⊥)). (snd p) (f (fst p)), и все доказательство:
(λf:A=>B) . (λp : A & (B=>⊥)). (snd p) (f (fst p))

Чтобы доказать в обратную сторону, нужно уметь из доказательства -(A & -B) получить доказательство A=>B. Первое есть импликация, избавиться от которой опять не получается, т.к. неоткуда взять аргумент. Поэтому в данной системе A=>B и -(A & -B) не эквивалентны, но по крайней мере из первого следует второе.

Пример 4. -A | B => (A => B)
Опять импликация, (λp : -A | B). f, где f : A => B.
Мы знаем, что p содержит либо доказательство -А, либо доказательство В. Проведем анализ вариантов:
f = cases p g h
g : -A => (A => B)
h : B => (A => B)
Построить h совсем просто: (λb:B) . (λa:A) . b.
А вот с g интересней.
g = (λn: -A) . (λa:A) . abortB(n a)
Имея доказательство a:A и доказательство n : -A, мы можем получить доказательство (n a) : ⊥ (противоречие). А из него по правилу для ⊥ мы можем получить все, что угодно, например, доказательство В. В итоге все доказательство выглядит так:
(λp : -A | B). cases p ((λn:-A).(λa:A).abortB(n a)) ((λb:B).(λa:A).b)
Такой трюк с извлечением доказательства из противоречия, вроде, вполне вписывается в систему, но мне кажется каким-то жульничеством. Если кто прояснит этот момент, буду признателен.

Везде выше мы говорили о высказываниях и доказательствах, но в приведенных правилах и формулах, я думаю, все уже узнали вариант типизированного лямбда-исчисления.




Действительно, из данной логической системы получается типизированное лямбда-исчисление простой заменой наименований: высказывания назовем типами, доказательства - термами (объектами), конъюнкцию - product type (tuple), дизъюнкцию - sum type, импликацию - функциональным типом, а правила упрощения - правилами вычисления. При этом первый пример дает нам карринг. Вот такой изоморфизм сами знаете кого.

Date: 2010-05-13 03:14 pm (UTC)
From: [identity profile] deni-ok.livejournal.com
В λ2 всё ещё прикольнее. Правила введения и удаления импликации → по прежнему соответствуют лямбда-абстракции и применению (как и в просто типизированной лямбде). Правила введения и удаления квантора общности ∀ соответствуют универсальным Λ-абстракции и применению (это уже специфика λ2):
           M:σ
 _____________________

  (Λ α . M):(∀α . σ)

     M:(∀α . σ)
 __________________

   (M τ):σ[α:=τ]

После чего все остальные логические связки выражаются так
⊥	≡	∀α . α 
¬σ	≡	σ → ⊥ 
σ ∧ τ	≡	∀α . (σ → τ → α) → α 
σ ∨ τ	≡	∀α . (σ → α) → (τ → α) → α 
∃α . σ	≡	∀β . (∀α . σ → β) → β


То есть ⊥ вводится как ненаселённый тип ∀α . α и, соответственно, операционные проблемы с abort решаются чисто синтаксически.

Введение ¬. Тип
(σ → τ) → (σ → ¬τ) → ¬σ = (σ → τ) → (σ → τ → ⊥) → σ → ⊥
Терм этого типа
λ f:σ→τ .  λ g:σ→τ→⊥ .  λ x:σ . g x (f x)
Это даёт доказательство приведением к нелепости — reductio ad absurdum.

Удаление ¬. Тип
σ→ ¬σ → τ = σ → (σ → ⊥) → τ = σ → (σ → ∀α . α) → τ
Терм этого типа
λ x:σ . λ f:σ→⊥ . f x τ
Из противоречия следует все что угодно.

Date: 2010-05-13 03:18 pm (UTC)
From: [identity profile] deni-ok.livejournal.com
Чёрт, забыл поставить правильный юзерпик :)

Date: 2010-05-13 03:50 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
Спасибо, помедитирую над этим..

Date: 2010-05-17 12:25 pm (UTC)
From: [identity profile] beroal.livejournal.com
Такой трюк с извлечением доказательства из противоречия, вроде, вполне вписывается в систему, но мне кажется каким-то жульничеством.

Суть в том, что у дизъюнкции два конструктора данных, а у абсурда ноль. Поэтому abort и cases являются частными случаями pattern matching. Поскольку у abort ноль конструкторов данных, то pattern matching содержит ноль веток. Поскольку тип pattern matching определяется типом веток, то тип abort не зависит ни от чего, то есть произвольный.

Похоже, это известные грабли. Есть упоминание в исторических трудах:
Объясняя, почему импликацию с ложной посылкой целесообразно считать истинной (что, по меньшей мере, неочевидно), В.А. приводил примерно такое рассуждение.

- Представьте себе, что я сказал: "провалиться мне на этом месте, если я вру!" Это значит "если я вру, то я провалюсь". Импликация. Убедительная сила подобных высказываний состоит в том, что они предполагаются истинными. Но ведь посылка-то ложна! Смотрите - В.А. осторожно (дело было на 16 этаже!) попробовал пол ногою - я же не проваливаюсь!

Наверное, с «из абсурда следует что угодно» надо смириться как с тем, что Земля круглая или теорией относительности. :)

Вот ещё один повод помедитировать. В GHCi:
Prelude> :m + Data.Function
Prelude Data.Function> :t fix id
fix id :: a

Получаем доказательство произвольного высказывания, то есть тривиальную логическую теорию.

Date: 2010-05-17 05:38 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
Меня больше смутило то, что, исходя из названия и описания, abort должен прекращать вычисления, но вместо этого его результат используется в дальнейших вычислениях, т.е. никакого abort'a на деле не происходит. И с вычислительной точки зрения нужно создать значение, но непонятно как, т.е. контруктивность какая-то неконструктивная. :)

Date: 2010-05-17 09:38 pm (UTC)
From: [identity profile] beroal.livejournal.com
Да, название странное. Хотя в математике привыкаешь и к совсем бессмысленным названием. Я бы назвал impossible. abort обозначает ветку, в которую невозможно попасть. Условия задачи таковы, что в run time туда никогда не попадём, хотя в compile time ветка имеется.

Допустим сказано, что длина списка не равна нулю. Вы делаете pattern matching списка и первая ветка соответствует пустому списку. Длина пустого списка равна нулю. Следовательно, ноль не равен нулю. Отсюда следует ⊥. Значит, в первой ветке никакого кода писать не нужно.

Таким образом на практике используется этот ⊥.

Date: 2010-05-18 03:21 am (UTC)
From: [identity profile] thedeemon.livejournal.com
О! makes sense! Спасибо.

Date: 2010-05-17 07:08 pm (UTC)
From: [identity profile] deni-ok.livejournal.com
Тип fix, как теорема некоторой логики (a -> a) -> a, наводит на нехорошие мысли об этой логике :)

Date: 2010-05-17 09:42 pm (UTC)
From: [identity profile] beroal.livejournal.com
Вообще-то fix — это математическая индукция. А какая ж логика без неё? (Это как фэнтези без эльфов.) ;)

Date: 2010-05-13 11:46 pm (UTC)
From: [identity profile] juan-gandhi.livejournal.com
По-моему, всё это можно выразить в семантике Крипке-Жуаяля, и получится то же самое.

Anyway, интересно, спасибо! Ещё один взгляд на (как мне кажется) одну и ту же вещь.

Date: 2010-05-15 07:40 pm (UTC)
From: [identity profile] thesz.livejournal.com
Прочитал с интересом.

Date: 2010-05-17 12:15 pm (UTC)
From: [identity profile] beroal.livejournal.com
Было бы неплохо ещё увидеть ссылку, что это называется интерпретация Брауэра-Гейтинга-Колмогорова. Значки логических связок: ∃∀¬⋀⋁∧∨→↔⊤⊥

просто полюбоваться фотографией типизированной лямбды в конце
Это что, у меня есть фотография монады.

Date: 2010-05-17 03:16 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
Не слышал такого названия раньше, в книжке, откуда черпаю, та троица не упоминается. Но да, в общем-то она.

Date: 2010-05-17 09:38 pm (UTC)
From: [identity profile] beroal.livejournal.com
Надеюсь, авторы книги не присваивают это открытие себе? :D

Date: 2010-05-18 03:23 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Нет, конечно. :)
Это я почитываю Simon Thompson "Type Theory & Functional
Programming".

Date: 2010-05-18 09:50 am (UTC)
From: [identity profile] beroal.livejournal.com
Ага, в книге оно под названием «4.4 The Curry Howard Isomorphism». В Википедии одним предложением сказано об их связи:
If one takes lambda calculus as defining the notion of a function, then the BHK interpretation describes the [Curry Howard] correspondence between natural deduction and functions.

Я в этих тонкостях не разбираюсь.

Profile

thedeemon: (Default)
Dmitry Popov

December 2025

S M T W T F S
 12 3456
789101112 13
14151617181920
21222324252627
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 30th, 2026 06:47 am
Powered by Dreamwidth Studios