Вы находитесь на странице: 1из 42

ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ АВТОНОМНОЕ ОБРАЗОВАТЕЛЬНОЕ

УЧРЕЖДЕНИЕ
ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ
«НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ
«ВЫСШАЯ ШКОЛА ЭКОНОМИКИ»
Факультет Санкт-Петербургская школа физико-математических и
компьютерных наук

Розплохас Дмитрий Александрович


СЕРТИФИЦИРОВАННАЯ СЕМАНТИКА
ЯЗЫКА MINIKANREN
Выпускная квалификационная работа
по направлению подготовки 01.04.02 Прикладная математика и информатика
образовательная программа «Анализ больших данных в бизнесе, экономике и
обществе»

Рецензент Научный руководитель


д. ф.-м. н., проф.
___________________ ____________________
А.А. Трунов А.В. Омельченко
Консультант
к. ф.-м. н., доцент
____________________
Д.Ю. Булычев
Оглавление
Введение 3

1. Обзор 5
1.1. Язык MiniKanren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2. Денотационная семантика языка MiniKanren . . . . . . . . . . . . . . . . 9
1.3. Существующие семантики логических языков
программирования . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2. Операционная семантика для поиска


с чередованием 14

3. Эквивалентность операционной и денотационной семантик 19

4. Формализация теории в Coq 23


4.1. Формализация необходимых понятий
из теории унификации в Coq . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.2. Формализация синтаксиса языка MiniKanren в Coq . . . . . . . . . . . . 25
4.3. Формализация денотационной семантики в Coq . . . . . . . . . . . . . . 27
4.4. Формализация операционной семантики в Coq . . . . . . . . . . . . . . . 28
4.5. Формализация доказательства эквивалентности семантик в Coq . . . . . 30

5. Прочие применения новой семантики 32


5.1. Корректность языковых преобразований . . . . . . . . . . . . . . . . . . . 32
5.2. Опровергающая полнота отношений . . . . . . . . . . . . . . . . . . . . . 33

Заключение 35

Список литературы 36

A. Детальные доказательства приведенных


утверждений 39
A.1. Доказательство леммы 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
A.2. Доказательство леммы 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
A.3. Доказательство леммы 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
A.4. Доказательство леммы 14 . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

2
Введение
MiniKanren — минималистичный встраиваемый язык логического программирова-
ния, свободный от побочных эффектов. Вычисления в нем основаны на идее поиска с
чередованием (interleaving search [2]), важной и известной характеристикой которого
является полнота (то есть свойство, что любой ответ, удовлетворяющий программе-
запросу, будет найден за конечное время). Чистота языка (в отношении побочных
эффектов) и специфические свойства поиска позволяют эффективно использовать
MiniKanren для реализации парадигмы “реляционного программирования”: функцио-
нальные программы начинают рассматриваться и конструироваться как математи-
ческие отношения между аргументами и результатом, что позволяет производить
обратимые вычисления. Эта эффективность (а также простота встраивания языка)
обеспечивает его популярность: существует более 100 его реализаций для более чем
40 различных языков1 .
Даже в академических текстах язык и его аспекты обычно представляются нефор-
мально. Главная книга [11], являющаяся введением в язык, описывает его с помо-
щью последовательности усложняющихся примеров программ. Статьи [6, 12, 29, 31],
описывающие различные расширения языка, представляют их реализацией на языке
Scheme. При таком подходе специфицировать язык и его расширения для каждой ре-
ализации нужно отдельно, полагаясь на знание языка реализации у читателя. Кроме
того, анализ программ и доказательства свойств языка, опирающиеся на такую спе-
цификацию, являются крайне сложными (так как необходимо учитывать множество
деталей исполнения программ в языке реализации) и могут считаться достоверными
только в рамках конкретной реализации.
Альтернативным общепринятым подходом к спецификации, который решает ука-
занные проблемы, является определение операционной семантики языка — абстракт-
ного описания процесса исполнения программ, которому должны соответствовать
конкретные реализации. В литературе уже описывались несколько вариантов такой
семантики для MiniKanren (см. обзор в разделе 1.3), однако все они имеют важное
ограничение: они не отражают свойств поиска с чередованием, поэтому являются
значительным упрощением по сравнению с общепринятым пониманием языка.
В данной работе будет представлена операционная семантика, свободная от дан-
ного ограничения, и доказано определяющее свойство поиска — его полнота — путем
установления эквивалентности этой операционной семантики и денотационной семан-
тики языка (которая задает математическую модель программы).
При этом, как это часто бывает для сколько-нибудь реальных языков програм-
мирования, описание и доказательства требуют особой аккуратности и являются до-
вольно сложными для проверки, потому что доказательства включают большое чис-
1
http://minikanren.org/#implementations

3
ло случаев и деталей, за которыми нужно следить (а также потому, что происходит
работа с бесконечными математическими объектами). Поэтому для обеспечения на-
дежности результата мы изначально задаемся целью создания “сертифицированной
семантики” — семантики, определение которой будет формализовано и все доказа-
тельства верифицированы в некоторой среде автоматической проверки доказательств
(мы используем Coq [3]). Это также позволит нам, используя механизм извлечения
программ из доказательств, получить готовую реализацию MiniKanren, гарантиро-
ванно соответствующую семантике и обладающую доказанными свойствами.

Постановка задачи
Целью данной работы является определение и демонстрация применения опера-
ционной семантики языка MiniKanren, задающей поиск с чередованием.
В рамках данной цели выделены следующие задачи.

1. Определить операционную семантику языка MiniKanren, опираясь на поиск с


чередованием.

2. Доказать полноту поиска используя семантику.

3. Реализовать и верифицировать определения и доказательства в интерактивной


среде доказательства теорем Coq и извлечь сертифицированную реализацию
MiniKanren.

4. Показать различные применения данной семантики для доказательства прочих


свойств исполнения программ в языке.

4
1. Обзор
В этом разделе будет представлено описание языка MiniKanren, включая его син-
таксис и денотационную семантику. Также будет дан обзор предшествующих работ,
посвященных описанию различных семантик (в том числе сертифицированных) как
для языка MiniKanren, так и в более широком контексте логического программиро-
вания.

1.1. Язык MiniKanren


Предпосылкой для языка MiniKanren стала работа [2], демонстрирующая как стан-
дартные примитивы поиска из логического программирования могут быть простым
образом реализованы в качестве монады на функциональном языке. Вскоре было по-
казано [11], что простой встраиваемый язык, основанный на такой реализации, очень
удобен для программирования в парадигме “программы как отношения” и позволяет
элегантно задавать решения переборных задач. Впоследствии MiniKanren был при-
менен для таких нетривиальных задач как генерация квайнов [7], синтез кода функ-
ции по заданным значениям [30], автоматический поиск доказательств теорем [21].
Некоторые из этих применений потребовали расширения языка новыми возможно-
стями, такими как поддержка номинальных термов [6] и неравенств (disequality con-
straints [31]), а также оптимизаций процесса исполнения программ, таких как задерж-
ка исполнения некоторых целей [12] и использование специальных структур данных
для устранения зацикливаний [27], что привело к большому разнообразию версий язы-
ка, имеющемуся сейчас. Мы будем работать с базовой версией языка (с той незначи-
тельной разницей, что оператор conde разделен на отдельные операторы конъюнкции
и дизъюнкции для удобства).
Синтаксис языка приведен на Рис. 1.
Все данные в языке представляются термами T с конструкторами из некоторого
множества заданных конструкторов C в качестве функциональных символов, которые
могут содержать переменные двух разных видов: синтаксические X или логические
A. Термы, не содержащие никаких переменных будем называть константными (и обо-
значать их множество D).
Основной синтаксической категорией в программе являются цели G. Цель задает
некоторое отношение, то есть наборы константных термов, которые нужно подставить
вместо логических переменных в цели, чтобы её выполнить. Базовой целью является
унификация двух термов, для которой при подстановке значений вместо логических
переменных два терма должны стать одинаковыми (при этом синтаксических пере-
менных в них уже не будет, вместо них всегда будет что-то подставлено ранее). Цели
можно компоновать при помощи дизъюнкций (хотя бы одна из целей должна быть
выполнена) и конъюнкций (обе цели должны быть выполнены), а также оператора

5
x ∈ X = {x, y, z, . . . } синтаксические переменные
α ∈ A = {γ1 , γ2 , γ3 , . . . } логические переменные
Ck ∈ C = {N il0 , Cons2 , . . . } конструкторы с указанной арностью
t ∈ T = x | α | C k (t1 , . . . , tk ) термы
rk ∈ R = {append3 , reverse2 , . . . } имена отношений с указанной арностью
g ∈ G = цели
t1 ≡ t2 унификация
| g1 ∧ g2 конъюнкция
| g1 ∨ g2 дизъюнкция
| fresh x . g введение свежей логической переменной
| rk (t1 , . . . , tk ) вызов отношения
p ∈ P = {riki = λ xi1 . . . xiki . gi ; }∗ g программы

Рис. 1: Синтаксис базового языка


FV (x) = ∅
FV (α) = {α} ∪
FV (C (t1 , . . . , tk )) =
k
i FV (ti )

FV (t1 ≡ t2 ) = FV (t1 ) ∪ FV (t2 )


FV (g1 ∧ g2 ) = FV (g1 ) ∪ FV (g2 )
FV (g1 ∨ g2 ) = FV (g1 ) ∪ FV (g2 )
FV (fresh x . g) = FV
∪ (g)
FV (rk (t1 , . . . , tk )) = i FV (ti )

Рис. 2: Множество свободных логических переменных для термов и целей

введения “свежей” логической переменной, не встречающейся больше нигде в про-


грамме, он используется как квантор существования. Также можно вызывать опре-
деленное в программе отношение, указывая его имя (из множества имен отношений
R) и какие термы подставить в качестве аргументов.
Любая программа p состоит из последовательности определений именованных от-
ношений (тело каждого отношения задается целью) и главной цели-запроса. При этом
все синтаксические переменные в программе должны быть связаны (либо оператором
fresh, либо являться аргументом задаваемого отношения), а в телах отношений не
должно быть логических переменных, они могут встречаться только в запросе и имен-
но подходящие значения для этих переменных и будут искаться.
На Рис. 2 и Рис. 3 стандартным образом определены множества свободных логи-
ческих переменных в цели и подстановка терма вместо синтаксической переменной в
цели, которые мы будем использовать.
Одновременно конструкция каждой цели задает и процедуру поиска решений.
Неформально эта процедура выглядит следующим образом. Состояние поиска со-

6
x [t/x] = t
y [t/x] = y, y ̸= x
α [t/x] = α
C k (t1 , . . . , tk ) [t/x] = C k (t1 [t/x], . . . , tk [t/x])
(t1 ≡ t2 ) [t/x] = t1 [t/x] ≡ t2 [t/x]
(g1 ∧ g2 ) [t/x] = g1 [t/x] ∨ g2 [t/x]
(g1 ∨ g2 ) [t/x] = g1 [t/x] ∧ g2 [t/x]
(fresh x . g) [t/x] = fresh x . g
(fresh y . g) [t/x] = fresh y . (g [t/x]), y ̸= x
k
(r (t1 , . . . , tk )) [t/x] = rk (t1 [t/x], . . . , tk [t/x])

Рис. 3: Подстановка переменной для термов и целей

стоит из подстановки (конечного отображения из логических переменных в термы,


включающие только логические переменные, мы будем использовать для них букву
σ и стандартные обозначения и операции из теории унификации [1]), задающей на-
копленную информацию о значениях логических переменных на данный момент, и
индекса, следующего за максимальным используемым для логических переменных
на данный момент (чтобы в качестве свежих логических переменных мы могли брать
переменные с индексом больше или равным этому). Каждая цель превращает произ-
вольное начальное состояние в лениво вычисляемый поток (lazy stream) состояний,
соответствующих решениям для этой цели. Для каждого типа цели это превращение
происходит так:

• Для унификации t1 ≡ t2 выполняется алгоритм унификации термов в контексте


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

• Для дизъюнкции g1 ∨ g2 обе цели применяются к изначальному состоянию, а по-


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

• Для конъюнкции g1 ∧ g2 первая цель применяется к начальному состоянию, а


к каждому состоянию из полученного потока применяется вторая цель и полу-
ченные потоки смешиваются (тоже с чередованием).

• Для оператора fresh x . g в цель g вместо переменной x подставляется логиче-


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

7
• Для вызова функции rk (t1 , . . . , tk ) в тело функции подставляются данные аргу-
менты и полученная цель применяется к состоянию.

Для данной цели-запроса после её применения к начальному состоянию (с пустой


подстановкой) мы получим поток ответов, в которых будут итоговые подстановки,
применяя которые к логическим переменным из запроса мы будем получать значе-
ние этой переменной в данном решении. При этом итоговое значение переменной из
запроса может содержать логические переменные, не связанные подстановкой (в том
числе сама эта переменная может быть не связана подстановкой). Это будет означать,
что решением являются все означивания, в которых вместо не связанных подстанов-
кой логических переменных подставлены произвольные константные термы. Таким
образом, каждый ответ может задавать целое множество подходящих означиваний.
Этот небольшой набор конструкций в языке позволяет задать любую вычислимую
функцию в качестве отношения, при этом функции, написанные на некотором функ-
циональном языке, могут быть простым регулярным образом преобразованы в такие
отношения (это даже может быть сделано автоматичеки [20]).
Рассмотрим в качестве примера следующую программу, в которой определяются
отношения конкатенации двух и списков и разворачивания списка, а в цели-запросе
записывается, что значение логической переменной γ1 должно быть списком-палин-
дромом (переходить при разворачивании в себя). Здесь для краткости мы опустим
пометки об арности и используем синтаксический сахар, при котором вложенные при-
менения оператора fresh записываются сокращенно: fresh x1 x2 x3 . g вместо
fresh x1 . fresh x2 . fresh x3 . g.
append =λ a b ab .
((a ≡ Nil) ∧ (ab ≡ b)) ∨
(fresh h t tb .
(a ≡ Cons (h, t)) ∧
(ab ≡ Cons (h, tb)) ∧
(append t b tb)
);
reverse =λ a ar .
((a ≡ Nil) ∧ (ar ≡ Nil)) ∨
(fresh h t tr .
(a ≡ Cons (h, t)) ∧
(reverse t tr) ∧
(append tr (Cons (h, Nil)) ar)
);
reverse γ1 γ1

8
Важное отличие в определении от соответствующих функций на функциональном
языке состоит в том, что задаваемые нами отношения имеют на один аргумент боль-
ше. Этот последний аргумент соответствует результату функции на функциональном
языке. В остальном же отношения строятся по такому же принципу, что и функции:
в обоих отношениях разбираются два случая пустого/непустого списка-аргумента, и
во втором случае производится рекурсивный вызов для хвоста списка (для простоты
приведено отношение, соответствующее тривиальной версии функции разворачива-
ния, но для хвосто-рекурсивной версии отношение можно построить точно так же).
Наличие результата в списке аргументов позволяет нам вызывать функцию не только
“в прямую сторону” (передавать аргументы-константы и искать результат), но и бо-
лее интересным образом, например требуя любые совпадающие аргумент и результат,
как в нашем запросе.
В результате исполнения данной программы мы получим бесконечный поток под-
становок, в которых переменной γ1 будут соответствовать следующие значения.
γ1 7→ Nil
γ1 7 → Cons (γ2 , Nil)
γ1 7 → Cons (γ2 , Cons (γ2 , Nil))
γ1 7 → Cons (γ2 , Cons (γ5 , Cons (γ2 , Nil)))
...
При этом переменные справа не связаны в результирующей подстановке, то есть
соответствуют произвольному константному терму (но вместо всех вхождений одной
переменной должен быть подставлен один и тот же терм). Таким образом, каждый
ответ задает в точности множество всех палиндромов определенной длины.

1.2. Денотационная семантика языка MiniKanren


В этом подразделе будет дано формальное определение денотационной семантики
языка MiniKanren, которая была описана ранее и которую мы будем использовать
в этой работе. Денотационные семантики (которые обычно обозначаются J•K) сопо-
ставляют каждой программе на языке некоторый математический объект, абстраги-
руясь при этом от процесса исполнения программ. В нашем случае, денотационная
семантика будет задавать, каким математическим отношениям соответствуют цели
в программе, и, соответственно, какому множеству решений соответствует запрос в
программе. Мы используем стандартный для логических языков подход к определе-
нию денотацинной семантики через построение наименьшей эрбрановой модели [19].
При определении мы фиксируем некоторую произвольную программу с набором
определений отношений Γ = {ri = λ xi1 . . . xiki . gi }i и определим денотационную се-
мантику для целей в рамках данной программы. При этом нас интетересуют только
корректные программы, поэтому мы будем предполагать, что в целях всегда вызы-

9
ваются отношения, для которых имеется определение в программе, и что при вызове
указывается правильное количество аргументов. Денотационной семантикой програм-
мы тогда будет денотационная семантика цели-запроса.
В качестве математической интерпретации целей естественно взять теоретико-мно-
жественные отношения как множество подходящих значений для логических пере-
менных, встречающихся в программе. Мы будем делать по сути именно это, но для
удобного композиционного построения наша семантика будет сопоставлять значения
всем логическим переменным, а не только встречающимся в программе (при этом, как
мы покажем позже, в результате только значения встречающихся переменных будут
важны). Таким образом доменом нашей денотационной семантики будут множества
означивающих функций, сопоставляющих каждой логической переменной некоторый
константный терм.

JgK ∈ 2A→D

Для определения нам понадобится несколько операций над означивающими функ-


циями (которые мы будем обозначать с помощью буквы f). Это стандартные опрера-
ции изменения значения функции в точке:
{
f(α′ ), α′ ̸= α
f[α ← d](α′ ) =
d, α′ = α
и гомоморфного расширения, позволяющего применить означивающую функцию к
термам (с логическими переменными):

f: T → D

f(α) = f(α)
k
f(C (t1 , . . . , tk )) = Ck (f(t1 ), . . . , f(tk ))
Кроме того, нам потребуется операция обобщения переменной в множестве означива-
ющих функций. Эта операция берет все функции из множества и позволяет заменить
значение данной переменной на произвольное.

F ↑ α = {f[α ← d] | f ∈ F, d ∈ D}

Неформально это соответсвует “забыванию” всех ограничений на конкретную пере-


менную.
Теперь мы можем определить денотационную семантику для целей содержащих
логические переменные (и не содержащих несвязанных синтаксических).
Определение денотационной семантики целей приведено на Рис. 4. Семантикой
унификации являются означивающие функции, делающие два данных терма равны-

10
Jt1 ≡ t2 K = {f : A → D | f (t1 ) = f (t2 )}
Jg1 ∨ g2 K = Jg1 K ∪ Jg2 K
Jg1 ∧ g2 K = Jg1 K ∩ Jg2 K
Jfresh x . gK = (Jg [α/x]K) ↑ α, для α ̸∈ F V (g)
Jrk (t1 , . . . , tk )K = Jg [t1 /x1 , . . . , tk /xk ]K, для (rk = λ x1 . . . xk . g) ∈ Γ

Рис. 4: Денотационная семантика целей

ми (через гомоморфное расширение). Дизъюнкция и конъюнкция интерпретируют-


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

Лемма 1. Для любой цели g с единственной несвязанной синтаксической перемен-


ной x, для любых двух логических переменных α1 и α2 , не являющихся свободными
в g, если f1 ∈ Jg [α1 /x]K, то для любой означивающей функции f2 , такой, что

1. f2 (α2 ) = f1 (α1 )

2. ∀α : α ̸= α1 ∧ α ̸= α2 , f2 (α) = f1 (α)

верно, что f2 ∈ Jg [α2 /x]K.

Доказательство. Структурная индукция по цели. Детали см. в приложении A.1.

Это утверждение кажется довольно естественным и на начальных этапах рассмат-


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

11
Другое важное свойство определенной так денотационной семантики, которое мы
обещали доказать, состоит в том, что принадлежность означивающей функции се-
мантике цели зависит только от значений функции на свободных переменных этой
цели. Мы будем называть это замкнутостью денотационной семантики.

Лемма 2. (Замкнутость денотационной семантики) Для любой цели g и любых


двух означивающих функций f1 and f2 , таких что f1 |F V (g) = f2 |F V (g) , верно что

f1 ∈ JgK ⇔ f2 ∈ JgK.

Доказательство. Индукция определению денотационной семантики.

Оба эти свойства потребуются нам в доказательствах в дальнейшем.

1.3. Существующие семантики логических языков


программирования
Изучение формальных семантик для логических языков программирования, в
первую очередь для Prolog, является устоявшейся исследовательской областью. Впер-
вые денотационные и операционные семантики были описаны одновременно в ранних
работах [9, 13], в которых также рассматриваются расширение языка конструкцией
отсечения. В последнее время стали также появляться работы, посвященные при-
менению методов компьютерной верификации к таким семантикам. В частности, в
статье [16] дается описание сертифицированной денотационной семантики для языка
Prolog расширенного конструкцией отсечения, а в статье [17] доказывается эквива-
лентность нескольких разных денотационных семантик для чистого Prolog; обе рабо-
ты включают формализацию на языке Coq.
Формальные семантики для языка MiniKanren также изучались ранее. Описанная
в предыдущем разделе денотационная семантика была разработана А. Вяткиным, но
не была опубликована. В недавних работах также представлены две операционные
семантики: в статье [20] описана недетерминированная семантика малого шага, а в
статье [26] описана семантика большого шага для конечных множеств решений.
Другая работа [18] по сертифицированному описанию семантики MiniKanren мо-
жет рассматриваться в качестве непосредственного предшественника нашей работы.
В ней также описываются денотационная и операционная семантики и преведено до-
казательство корректности операционной относительно денотационной, сертифици-
рованное с помощью системы доказательства теорем HOL. Денотационная семантика
схожа с описаной выше, но допускает только программы с единственной переменной
в запросе. Операционная семантика является недетерменированной и схожа с пред-
ставленной в [20]. Кроме того, описана “исполнимая” (executable) семантика, которая

12
может использоваться в качестве реального интерпретатора языка, но её связь с дву-
мя другими не устанавливается.
Таким образом, ни одна из предыдущих известных нам семантик не описывает по-
иска с чередованием, поэтому не пригодна для доказательства его главного свойства —
полноты — и получения сертифицировано корректного интерпретатора. Единственное
известное нам относительно строгое (но не сертифицированное) доказательство этого
свойства представлено в статье [28], однако оно дается только в рамках конкретной
реализации поиска на языке Scheme, и, кроме того, полнота поиска понимается там
как сохранение всех ответов при смешивании потоков, то есть в более узком смысле,
чем наше понимание полноты здесь — как соответствие множества найденых ответов
математической модели программы.

13
2. Операционная семантика для поиска
с чередованием
В этом разделе будет описана новая операционная семантика языка MiniKanren,
отражающая исполнение программ в некоторой реальной реализации языка, в кото-
рой осуществляется поиск решений с чередованием.
Мы зададим семантику в стандартной форме системы переходов с пометками
(labeled transition system, LTS [14]). А именно, мы определим множество состояний по-
иска и зададим переходы между состояниями, некоторые из которых будут помечены
ответом, найденым на данном шаге. Исполнение программы тогда будет задаваться
путем в таком графе из некоторого начального состояния, а пометки на переходах в
этом пути будут соответствовать ответам, которые находит поиск. При определении
семантики мы снова зафиксируем программу с некоторым произвольным набором
определений отношений Γ = {ri = λ xi1 . . . xiki . gi }i .
Множество нетерминальных состояний определяется индуктивно следующим об-
разом:

s ∈ S = ⟨g, σ, n⟩ | s1 ⊕ s2 | s ⊗ g.

Базовым нетерминальным состоянием является тройка ⟨g, σ, n⟩ обозначающее за-


дание выполнить цель g с начальной подстановкой σ и c начальным счетчиком заня-
тых логических переменных (более конкретно, это индекс первой незанятой логиче-
ской переменной, все следующие за которой также незаняты). Также нетерминальное
состояние может описывать частичного выполненную дизъюнкцию (⊕) или конъюнк-
цию (⊗). Таким образом нетерминальное состояние задает частичное дерево исполне-
ния цели, в листьях которого находятся ещё не выполненные цели. Определение отра-
жает характер исполнения разных типов целей в MiniKanren: дизъюнкции выполня-
ются параллельно, поэтому обоими операндами ⊕ являются другие нетерминальные
состояния, а конъюнкции выполняются последовательно, поэтому нетерминальным
состоянием является только первый операнд, в котором будут производиться вычис-
ления сначала.
В полное множество состояний к нетермальным состояниям добавляется одно тер-
минальное (которое мы будем обозначать символом ♢), обозанчающее, что поиск за-
вершен и множество решений исчерпано.

ŝ ∈ Ŝ = ♢ | s

При этом имеет смысл рассуждать только о согласованных (well-formed) состо-


яниях, в которых встречаются только занятые переменные. Такие состояния опре-
деляются следующим образом (здесь Dom (σ) обозначает множество переменных, на

14
которых определена подстановка, а VRan (σ) — множество переменных, встречаю-
щихся в значениях подстановки).

Определение 1. Условие согласованности состояний:

• ⋄ согласовано;

• ⟨g, σ, n⟩ согласовано, когда FV (g) ∪ Dom (σ) ∪ VRan (σ) ⊂ {γ1 , . . . , γn };

• s1 ⊕ s2 согласовано, когда s1 и s2 согласованы;

• s ⊗ g согласовано, когда s согласовано и для всех тройек ⟨_, _, n⟩ являющихся


листьями в s верно FV (g) ⊆ {γ1 , . . . , γn }.

Начальным состоянием для запроса g, индексы логических переменных в котором


ограничены числом n, будет нетерминальное состояние ⟨g, ϵ, n⟩ (где ϵ обозначает пу-
стую подстановку) из одной невыполненной цели. Такое состояние очевидно всегда
будет согласованным.
Переходы между состояниями также определим индуктивно. Это может быть
непомеченный переход s → − ŝ или переход, помеченный ответом, в который входит
итоговая подстановка и итоговый индекс первой незанятой логической переменной,
(σ,n)
s −−−→ ŝ. Правила, задающие переходы, приведены на Рис. 5.
Для базового состояния с целью-унификацией мы всегда переходим в терминаль-
ное состояние. При этом мы пытаемся унифицировать данные термы и, если унифи-
кация проходит успешно, возвращаем композицию начальной подстановки с наиболь-
шим общим унификатором в качестве ответа.
Для базового состояния с дизъюнкцией мы создаем частично вычисленную дизъ-
юнкцию с заданиями выполнить обе цели в том же контексте, с конъюнкцией — ча-
стично вычисленную конъюнкцию (при этом вычисляться будет только левая цель
конъюнкции, а правая откладывается).
Для базового состояния с оставшимися двумя видами целей мы переходим в новое
базовое состояние с той же подстановкой, но измененными целями: для введения све-
жей переменной мы подставляем первую незанятую логическую переменную в цель
(и увеличиваем счетчик в состоянии), для вызова отношения раскрываем его опреде-
ление.
Оставшиеся правила описывают процесс вычисления в частичных дизъюнкциях и
конъюнкциях. Порядок вычисления аргументов здесь как раз задает поиск с чередо-
ванием в его стандартной форме.
Для частично вычисленной дизъюнкции ⊕ мы всегда делаем первый шаг в левом
состоянии, после чего меняем состояния местами. Все полученные при этом ответы
возвращаются, и, если шаг вычислений левого сотояния переходит в терминальное
состояние, то остается только правое.

15
⟨t1 ≡ t2 , σ, n⟩ −
→ ♢, ∄ mgu (t1 σ, t2 σ) [UNIFYFAIL]

(σδ,n)
⟨t1 ≡ t2 , σ, n⟩ −−−→ ♢, mgu (t1 σ, t2 σ) = δ [UNIFYSUCCESS]

⟨g1 ∨ g2 , σ, n⟩ −
→ ⟨g1 , σ, n⟩ ⊕ ⟨g2 , σ, n⟩ [DISJ]

⟨g1 ∧ g2 , σ, n⟩ −
→ ⟨g1 , σ, n⟩ ⊗ g2 [CONJ]

⟨fresh x . g, σ, n⟩ −
→ ⟨g [αn /x], σ, n + 1⟩ [FRESH]

(rk = λ x1 . . . xk . g) ∈ Γ
[INVOKE]
⟨rk (t1 , . . . , tk ), σ, n⟩ −
→ ⟨g [t1 /x1 ] . . . [tki /xki ], σ, n⟩
s1 −
→♢
[DISJSTOP]
(s1 ⊕ s2 ) −
→ s2
r
s1 −
→♢
r [DISJSTOPANS]
(s1 ⊕ s2 ) −
→ s2
s1 −→ s′1
[DISJSTEP]
→ (s2 ⊕ s′1 )
(s1 ⊕ s2 ) −
r
→ s′1
s1 −
r [DISJSTEPANS]
→ (s2 ⊕ s′1 )
(s1 ⊕ s2 ) −
s−→♢
[CONJSTOP]
(s ⊗ g) −
→♢
(σ,n)
s −−−→ ♢
[CONJSTOPANS]
(s ⊗ g) −
→ ⟨g, σ, n⟩
s−→ s′
[CONJSTEP]
(s ⊗ g) −
→ (s′ ⊗ g)
(σ,n)
s −−−→ s′
[CONJSTEPANS]
(s ⊗ g) →
− (⟨g, σ, n⟩ ⊕ (s′ ⊗ g))

Рис. 5: Система переходов для поиска с чередованием

16
Для частично вычисленной конъюнкции ⊗ шаги всегда делаются в левом состоя-
нии, а любой полученный при этом ответ (подстановка и счетчик переменных) ком-
понуется в новое состояние с правой целью и начинает вычисляться параллельно с
данной частично вычисленной конъюнкцией (для этого они объединяются с помо-
щью ⊕). Когда вычиление левого состояния доходит до терминального состояния,
вычисление частичной конъюнкции завершается.
Несложно заметить, что данная система переходов является детерминированной:
из любого нетерминального состояния существует ровно один переход. Таким обра-
зом, для любой программы действительно всегда существует единственный путь из
начального состояния, задающий её исполнение. Причем мы описываем исполнение
как завершающихся программ (если путь приходит в терминальное состояние), так и
незавершающихся (если путь бесконечен). Мы будем обозначать множество ответов
на таком пути из состояния ŝ как T r(ŝ).
Также легко показать, что состояния после перехода остаются согласованными.

Лемма 3. Для любого согласованного нетерминального состояния s, если из него


(σ,n)
есть переход s −
→ ŝ или s −−−→ ŝ, то состояние ŝ согласовано.

Доказательство. Индукция по определению перехода.

Двумя следующими леммами мы можем описать, как множества ответов, получа-


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

Лемма 4. Для любых нетерминальных состояний s1 и s2 ,

T r(s1 ⊕ s2 ) = T r(s1 ) ∪ T r(s2 ).

Доказательство. Индукция по количеству переходов до ответа.

Лемма 5. Для любых нетерминального состояния s и цели g,



T r(s ⊗ g) = T r(⟨g, σ, n⟩).
(σ,n)∈T r(s)

Доказательство. Индукция по количеству переходов до ответа.

Из леммы 4 напрямую следует базовое свойство языка MiniKanren — справедли-


вость исполнения дизъюнкции (fair disjunction).

Следствие 1 (Справедливость исполнения дизъюнкции). Для любых целей g1 и g2 ,


подстановки σ и счетчика n,

T r(⟨g1 ∨ g2 , σ, n⟩) = T r(⟨g1 , σ, n⟩) ∪ T r(⟨g2 , σ, n⟩).

17
Кроме того, мы можем также легко описать условие завершимости исполнения
дизъюнкции.

Лемма 6. Для любых целей g1 и g2 , подстановки σ и счетчика n, путь из состо-


яния ⟨g1 ∨ g2 , σ, n⟩ конечен тогда и только тогда, когда конечны путь из состояния
⟨g1 , σ, n⟩ и путь из состояния ⟨g2 , σ, n⟩.

Доказательство. Индукция по длине пути.

Из этого напрямую следует другое важное свойство языка — коммутативность


дизъюнкции (disjunction commutativity), которая говорит, что при изменении порядка
аргументов в дизъюнкции завершимость программы не меняется.

Следствие 2 (Коммутативность дизъюнкции). Для любых целей g1 и g2 , подстанов-


ки σ и счетчика n, путь из состояния ⟨g1 ∨ g2 , σ, n⟩ конечен тогда и только тогда,
когда конечен путь из состояния ⟨g2 ∨ g1 , σ, n⟩.

18
3. Эквивалентность операционной и денотационной
семантик
В этом разделе мы докажем эквивалентность определенной в предыдущем разделе
операционной семантики языка MiniKanren и его денотационной семантики, описан-
ной в разделе 1.2.
Таким образом мы формализуем и докажем полноту поиска с чередованием, по-
тому что операционная семантика задает ответы, которые будут найдены конкретно
этим поиском, а денотационная семантика задает все ответы, лежащие в математи-
ческой модели программы, построенной по запросу в ней.
Но прежде чем мы сможем сформулировать эквивалентность этих двух семантик,
нам необходимо установить соответствия между разными формами представления
ответов в них: для операционной семантики это подстановка, а для денотационной —
множество означивающих функций.
Для этого заметим, что любую подстановку σ можно расширить до означивающей
функции, которая удовлетворяет ограничениям на значения переменных в σ, взяв
композицию σ и произвольной означивающей функции. Тогда мы можем определить
денотационный аналог подстановки (соответствующее ей множество означивающих
функций) следующим образом:

JσK = {f ◦ σ | f : A 7→ D}.

А денотационным аналогом операционной семантики для состояния ŝ будет объ-


единение денотационных семантик подстановок всех ответов из операционной семан-
тики:


JŝKop = JσK
(σ,n)∈T r(ŝ)

Теперь мы можем перейти к эквивалентности семантик. Мы разделим её на тео-


ремы о корректности (soundness) и полноте (completeness) операционной семнантики
относительно денотационной.

Теорема 1 (Корректность операционной семантики). Если для цели g верно


FV (g) ⊆ {γ1 , . . . , γn }, то

J⟨g, ϵ, n⟩Kop ⊆ JgK.

Теорема может быть доказана по индукции, но сначала нам нужно обобщить её,
чтобы индукционная гипотеза стала достаточно сильной для доказательства. Для
этого мы обобщим определение денотационной семантики целей на произвольные со-
стояния. Определение приведено на Рис. 6. Оно кажется достаточно естественным,

19
J♢K = ∅
J⟨g, σ, n⟩K = JgK ∩ JσK
Js1 ⊕ s2 K = Js1 K ∪ Js2 K
Js ⊗ gK = JsK ∩ JgK

Рис. 6: Денотационная семантика состояний

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

Лемма 7. Для любого согласованного состояния s

JsKop ⊆ JsK.

Она доказывается индукцией по количеству шагов до того момента, как некото-


рый ответ встретится на пути из данного состояния. Мы разобъем это доказательство
на два утверждения: для любого перехода в операционной семантике денотационная
семантика и пометки-ответа (если она есть) и следующего состояния лежат в денота-
ционной семантике начального состояния.

Лемма 8 (Корректность ответа). Для любого согласованного нетерминального со-


(σ,n)
стояния s и любого перехода s −−−→ ŝ,

JσK ⊆ JsK.

Доказательство. Индукция по определению перехода.

Лемма 9 (Корректность следующего состояния). Для любого согласованного нетер-


(σ,n)
минального состояния s и любого перехода s →
− ŝ или s −−−→ ŝ,

JŝK ⊆ JsK.

Доказательство. Индукция по определению перехода.

Логично было бы сформулировать теорему о полноте операционной семантики


как теорему о корректности с обращенным вложением, но, к сожалению, она невер-
на в такой общей форме. Так происходит потому, что денотационная семантика за-
дает только ограничения на свободные переменные цели (что отражено в свойстве
замкнутости денотационной семантики), тогда как в операционной могут ограничи-
ваться также значения переменных, вводимых в процессе поиска в качестве свободных
(и поэтому операционная семантика может содержать меньше означивающих функ-
ций). Поэтому при формулировке полноты мы будем рассматривать только значения

20
означивающих функций на переменных занятых в самом начале (включая все сво-
бодные переменные запроса). Такое уточнение формулировки никак не противоречит
нашей мотивации доказать полноту поиска в языке, так как ответы в MiniKanren
предоставляются только для переменных из запроса.

Теорема 2 (Полнота операционной семантики). Если для цели g верно


FV (g) ⊆ {γ1 , . . . , γn }, то

{f|{γ1 ,...,γn } | f ∈ J⟨g, ϵ, n⟩Kop } ⊇ {f|{γ1 ,...,γn } | f ∈ JgK}.

Как и в случае корректности, данную теорему можно доказать по индукции после


обобщения формулировки. В этот раз достаточно будет обобщить её с целей на состо-
яния вида ⟨g, σ, n⟩. Нам также нужно ввести ещё одну вспомогательную семантику
для целей — ограниченную денотанционную семантику:

J•Kl : G → 2A→D .

В этой семантике вместо того чтобы всегда раскрывать определение отношения


при вызове мы будем делать это только заданное число раз. Соответственно, для
данного набора определений отношений Γ = {riki = λ xi1 . . . xiki . gi }i , определение огра-
ниченной денотационной семантики в точности повторяет опеределение обычной де-
нотационной семантики целей, кроме случая вызова отношения, в котором определе-
ние выглялит следующим образом:

Jriki (t1 , . . . , tki )Kl+1 = Jgi [t1 /xi1 , . . . , tki /xiki ]Kl .

А для нулевого уровня семантику удобно определить как пустое множество:

JgK0 = ∅.

Ограниченная денотационная семантика является аппроксимацией обычной дено-


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

Лемма 10. Для любой цели g,



JgK ⊂ JgKl .
l

Доказательство. Индукция по определению денотационной семантики.

Теперь мы можем сформулировать и доказать обобщенную версию теоремы о пол-


ноте операционной семантики.

21
Лемма 11. Для любого согласованного состояния ⟨g, σ, n⟩, для любого уровня l

{f|{γ1 ,...,γn } | f ∈ J⟨g, σ, n⟩Kop } ⊇ {f|{γ1 ,...,γn } | f ∈ J⟨g, σ, n⟩Kl }.

Доказательство. Индукция по уровню, затем по цели. Детали см. в приложении A.2.

22
4. Формализация теории в Coq
Вся теория для языка MiniKanren, описанная в предыдущих разделах была форма-
лизована2 в среде интерактивных докзательств теорем Coq: синтаксис языка, денота-
ционная и операционная семантики и доказательство их эквивалентности. Код форма-
лизации находится в приведеном репозитории в папке src. Там же в папке extracted
находится код корректного по построению интерпретатора языка MiniKanren, извле-
каемый из этой формализации. В данном разделе будет поэтапно описана данная
формализация.
Формализация в целом довольно точно следует определениям и утверждениям,
приведенным в предыдущих разделах. Однако на язык накладывается одно неприн-
ципиальное ограничение, которое при этом значительно упрощает работу с ним в
Coq: всем конструкторам в термах разрешается иметь только арности равные ну-
лю или двум, а все задаваемые в программе отношения должны иметь ровно один
аргумент. Это ограничение никак не влияет на выразительность языка, так как после-
довательность термов произвльной длины всегда может быть закодирована списком,
используя конструкторы Nil0 и Cons2 . Кроме того, в формализации на Coq мы можем
отказаться от различения синтаксичених и логических переменных: явным образом
в термах задаются только логические переменные, а для обращения со связывани-
ем синтаксических переменных используется механизм синтаксиса высшего порядка
(higher-order abstract syntax [23]).
Но прежде чем перейти к формализации понятий языка MiniKanren, нам необхо-
димо сначала формализовать все базовые понятия из теории унификации, которые
используются в языке и приведенных семантиках.

4.1. Формализация необходимых понятий


из теории унификации в Coq
Формализация необходимых понятий из теории унификации содержится в файле
Unify.v.
Мы предполагаем, что имена всех сущностей в нашей теории берутся из неко-
торого счетного множества, поэтому в качестве типа имен мы будем использовать
натуральные числа.
Definition name := nat.

Первое базовое понятие, которое мы используем — это термы. Мы определяем их


индуктивно, как сказано выше они могут быть сконструированы только из логических
переменных и конструкторов арности ноль или два.
2
https://github.com/rozplokhas/miniKanren-coq

23
Inductive term : Set :=
| Var : name → term
| Cst : name → term
| Con : name → term → term → term.
Для термов мы задаем множество свободных переменных функцией, вычисляю-
щей его (она возвращает множество в форме Lists.ListSet.set). Также мы опреде-
ляем тип константных термов как зависимую пару, накладывая на обычные термы
условие отсутствия свободных переменных.
Definition ground_term : Set := {t : term | fv_term t = var_set_empty}.

Второе базовое понятие, необходимое нам — подстановки. Мы задаем их списка-


ми связываний (то есть пар из переменной и терма, в которую данная переменная
переводится).
Definition subst : Set := list ( name * term).
Для них мы определяем две базовые операции — функцию apply_subst, применя-
ющую такую подстановку к терму, и функцию compose, вычисляющую композицию
двух подстановок, — и доказываем ожидаемые свойства этих операций.
Ещё одной основополагающей частью процесса исполнения программ в языке Mini-
Kanren является вычисление наибольшего общего унификатора (most general unifier,
MGU). Реализация алгоритма вычисления MGU на языке с зависимыми типами яв-
ляется известной нетривиальной задачей. Главная сложность здесь состоит в том, что
языки с зависимыми типами, если они используются в качестве средства формалза-
ции математических понятий, обычно позволяют определять только гарантированно
завершающиеся функции, и в связи с этим допускают только структурную рекурсию
при определении (то есть у функции должен быть аргумент, размер значения которо-
го уменьшается при каждом рекурсивном вызове некоторым естественным образом).
Это верно и для Coq. При этом все основные известные алгоритмы вычисления MGU
не удовлетворяют этому свойству — в них всегда может произойти рекурсивный вы-
зов, при котором термы-аргументы увеличиваются.
В нашей формализации для некоторого удобства использования мы определяем
алгоритм вычисления MGU не как функцию, а как отношение (связывающее два
терма-аргумента и результат, который является либо итоговым унификатором, либо
сигналом неудачи), что позволяет пользоваться индукцией более свободно и избежать
описанной проблемы при определении.

Inductive mgu : term → term → option subst → Set := ...


Однако это не избавляет нас от проблемы, а только отсрочивает её появление: для
доказательства существования соответствующего результата для любой пары термов

24
и его определяющих свойств мы по точно тем же причинам не можем воспользоваться
обычной структурной индукцией.
Стандартным подходом для решения этой проблемы, который используется в част-
ности в работах [4, 15, 22, 25], является задание некоторой метрики на аргументах
функции вычисления MGU (обычно базирующейся на высоте термов и количестве
свободных переменных в них) и использование фундированной рекурсии (well-founded
recursion) при определении. Мы тоже используем подход такого рода. Конкретно, мы
отдельно определяем структурно-рекурсивную функцию, которая производит один
шаг унификации (находит переменную и неравный ей терм на соответствующих ме-
стах в аргументах и добавляет это связывание в унификатор) и доказываем, что после
каждого такого успешного шага число свободных переменных в термах (это наш про-
стой порядок) строго уменьшается.
После этого мы можем использовать фундированную индукцию (well-founded in-
duction) с указанным порядком для доказательства существования уникального ре-
зультата для любой пары аргументов и определяющих свойств этого результата,
которые мы будем использовать в дальнейших доказательствах: если вычисление
MGU успешно, то результирующая подстановка действительно унифицирует термы
(mgu_unifies) и является наиболее общей с таким свойством (mgu_most_general),
если же вычисление заканчивается неудачей, то унификаторов данных термов не су-
ществует (mgu_non_unifiable).

4.2. Формализация синтаксиса языка MiniKanren в Coq


Формализация синтаксиса содержится в файле MiniKanrenSyntax.v.
В формализации мы добавляем один вспомогательный тип целей — неудачу, кото-
рая соответствует пустому отношению и дает пустой поток. В результате, определение
типа целей выглядит следующим образом.
Inductive goal : Set :=
| Fail : goal
| Unify : term → term → goal
| Disj : goal → goal → goal
| Conj : goal → goal → goal
| Fresh : ( name → goal) → goal
| Invoke : name → term → goal.
В этот раз нам удобнее задать множество свободных переменных отношением, а не
функцией (потому что мы не будем использовать его в вычислительном контексте).
Inductive is_fv_of_goal (n : name) : goal → Prop := ...

25
Определение отношения в программе отражает упомянутое выше ограничение на
один аргумент.
Definition rel : Set := term → goal.
Как видно из определений выше для обращения со связываниями синтаксических
переменных мы пользуемся синтаксисом высшего порядка. Мы предпочли его синтак-
сису первого порядка, потому что он дает нам возможность использовать подстановку
и индукционный принцип для целей, которые предоставляет Coq. Кроме того, иметь
только один тип явно задаваемых переменных гораздо проще. С другой стороны, с
синтаксисом высшего порядка нам приходится явно требовать некоторые ограниче-
ния на наши синтаксические категории, которые в случае синтаксиса первого порядка
были бы выполнены всегда.
А именно, мы должны потребовать, чтобы все связывания переменных были “кон-
систентными”, то есть чтобы при подстановке разных переменных в это связывание
результаты были одинаковы с точностью до переименования этих переменных (при
условии, что переменные свободны в теле связывания). Эта излишняя неограничен-
ность является известной проблемой синтаксиса высшего порядка и к ней обращается
ранняя работа посвященная его использованию в Coq [10], называя её наличием экзо-
тических термов (exotic terms). Для её решения там вводится специальный предикат,
исключающий экзотические термы. Так же поступим и мы.
Формальное определение стандартного переименования переменных оказалось до-
вольно сложной задачей для нашего языка в случае fresh конструкций, поэтому мы
ограничимся ослабленной версией переименования, которая оперирует только с несво-
бодными в цели переменными, чего будет достаточно для нашего случая.
(* Weak version of a variable renaming *)
Inductive renaming (old_x : name) ( new_x : name) : goal → goal → Prop :=
...
| rFreshNFV : ∀ fg,
(~ is_fv_of_goal old_x (Fresh fg)) →
renaming old_x new_x ( Fresh fg) ( Fresh fg)
| rFreshFV : ∀ fg rfg,
( is_fv_of_goal old_x (Fresh fg)) →
( ∀ y, (~ is_fv_of_goal y (Fresh fg)) →
renaming old_x new_x ( fg y) ( rfg y)) →
renaming old_x new_x ( Fresh fg) ( Fresh rfg)
...
Тогда консистентность целей и отношений может быть определена следующим
образом.

26
Definition consistent_binding (b : name → goal) : Prop :=
∀ x y, (~ is_fv_of_goal x (Fresh b)) → renaming x y (b x) ( b y).
Inductive consistent_goal : goal → Prop := ...
Definition consistent_function (f : term → goal) : Prop :=
∀ a1 a2 t, renaming a1 a2 ( f t) ( f ( apply_subst [( a1, Var a2)] t)).
Definition consistent_rel (r : rel) : Prop :=
∀ ( arg : term), consistent_goal (r arg) ∧ consistent_function r.
В коде выше предикат consistent_goal индуктивно проверяет, что все связы-
вания в цели являются консистентными, а применение подстановки в определении
consistent_function используется для переименования переменной.
Кроме консистентности связываний, мы должны явно потребовать отсутствия
несвязанных переменных в целях.
Definition closed_goal_in_context (c : list name) ( g : goal) : Prop :=
∀ n, is_fv_of_goal n g → In n c.
Definition closed_rel (r : rel) : Prop :=
∀ ( arg : term), closed_goal_in_context (fv_term arg) (r arg).
Используя эти два требования, мы фиксируем здесь в виде аксиомы программу
с произвольным набором именованных определений отношений, который мы будем
использовать далее на протяжении нашей формализации. Наличие целей-неудач поз-
воляют нам задать это окружение в виде всюду определенной функции.
Definition def : Set := {r : rel | closed_rel r ∧ consistent_rel r}.
Definition spec : Set := name → def.
Axiom Prog : spec.

4.3. Формализация денотационной семантики в Coq


Формализация денотационной семантики содержится в файле DenotationalSem.v.
Мы можем определить означивающие функции просто как функции в Coq.
Definition repr_fun : Set := name → ground_term.

Мы также определяем для них операции применения к терму apply_repr_fun и


композиции с подстановкой subst_repr_fun_compose и доказываем достаточно много
простых технических лемм для работы с ними.
После этого мы определяем денотационную семантику целей с помощью индук-
тивного отношения in_denotational_sem_goal (для которого вводится обозначение
“ [| g , f |] ”), такого что:

∀g, f : in_denotational_sem_goal g f ⇐⇒ f ∈ JgK.

27
Здесь же мы определяем нашу вспомогательную ограниченную семантику целей
из раздела 3 с помощью аналогичного отношения in_denotational_sem_lev_goal и
показываем её взаимосвязь с обычной семантикой.
Далее мы определям денотационный аналог подстановки аналогичным отношени-
ем in_denotational_sem_subst и показываем его свойства.
Наконец мы формализуем доказательства леммы 2 о свойстве замкнутости дено-
тационной семантики и леммы 1 об изменении денотационной семантики при замене
подставляемой свежей переменной.

4.4. Формализация операционной семантики в Coq


Для формализации операционной семантики нам нужно прежде формализовать
ещё одно базовое понятие, используемое ей, — потенциально бесконечные потоки. Их
формализация содержится в файле Stream.v.
Как обычно, потоки определяются как коиндуктивная структура данных.
Context {A : Set}.
CoInductive stream : Set :=
| Nil : stream
| Cons : A → stream → stream.
Нам также нужно коиндуктивно определить специальное отношение равенства
потоков вместо стандартного синтаксического равенства, которое не получится ис-
пользовать в коиндуктивных доказательствах. Это стандартный подход для работы
с коиндуктивными структурами данных, описанный в [8].
CoInductive equal_streams : stream → stream → Prop :=
| eqsNil : equal_streams Nil Nil
| eqsCons : ∀ h1 h2 t1 t2, h1 = h2 →
equal_streams t1 t2,
equal_streams (Cons h1 t1) ( Cons h2 t2).
При этом некоторые важные свойства потоков будут осмыслены, только если опре-
делять эти свойства индуктивно. Это в частности относится к двум необходимым нам
предикатам принадлежности элемента потоку in_stream и конечности потока finite.
Другое важное для нас свойство потока (определяемое коиндуктивно) — являться
поэлементным чередованием двух других потоков.

CoInductive interleave : stream → stream → stream → Prop :=


| interNil : ∀ s s’, equal_streams s s’ → interleave Nil s s’
| interCons : ∀ h t s rs, interleave s t rs →
interleave (Cons h t) s ( Cons h rs).

28
Это определение позволяет нам в общем случае произвольных потоков доказать
принципиальные свойства поиска с чередованием, сформулированные ранее: мы дока-
зываем что поток-чередование конечен тогда и только тогда, когда конечны чередуе-
мые потоки, из чего следует лемма 6, и что элементами потока-чередования являются
в точности элементы чередуемых потоков, из чего следует лемма 4.
После этой предварительной работы мы можем переходить к формализации самой
операционной семантики. Она содержится в файле OperationalSem.v.
Прежде всего мы записываем индуктивные определения для нетерминальных со-
стояний nt_state и произвольных состояний state, а также для свойства согласо-
ванности нетерминальных состояний well_formed_nt_state.
Далее мы записываем нашу систему переходов в виде индуктивного отношения
естественным образом. При этом в каждый переход входит либо пометка-ответ, либо
явное обозначение непомеченного шага.

(* Labels *)
Inductive label : Set :=
| Step : label
| Answer : subst → nat → label.
(* Transitions *)
Inductive eval_step : nt_state → label → state → Set := ...
Мы устанавливаем её детерминированность, доказывая, что из любого нетерми-
нального состояния существует переход (Lemma eval_step_exists) и что этот пере-
ход (то есть комбинация из пометки на переходе и следующего состояния) уникален
(Lemma eval_step_unique). Кроме того, мы доказываем лемму 3 о сохранении согла-
сованности состояния при переходе.
После этого мы определяем саму операционную семантику как коиндуктивное от-
ношение op_sem между состоянием и потенциально бесконечным потоком, содержа-
щим пометки на пути и пропуски на месте непомеченных переходов.
Definition trace : Set := stream label.
CoInductive op_sem : state → trace → Set :=
| osStop : op_sem Stop Nil
| osNTState : ∀ nts l s t, eval_step nts l s →
op_sem s t →
op_sem ( NTState nts) ( Cons l t).
Мы также доказываем существование и уникальность (с коиндуктивно заданным
равенством) соответсвующего потока для любого состояния и формализуем определе-
ние денотационного аналога операционной семантики из раздела 3 (для него вводится
обозначение “{ | t , f |}”).

29
Наконец, мы формализуем доказательства лемм 4, 5 и 6 из раздела об операцион-
ной семантике.
Используя механизм экстракции кода из доказательств в Coq, мы извлекаем код
интерпретатора (функции, возвращающей поток ответов по начальному состоянию)
из доказательтва существования операционной семантики любого состояния. Этот ин-
терпретатор реализует определенную только что семантику по построению, а форма-
лизация доказательства корректности и полноты семантики, описанная далее, делает
его сертифицировано корректным (то есть дающим ответы, в точности отражающие
денотационную семантику).
Код на языке Haskell извлекается в файл extracted/interpreter.hs, а в соседнем
файле extracted/interpreter_wrapped.hs к этому коду дописываются несколько
простых примитивов для более удобного его использования (транслирующих обычные
натуральные числа и термы в наше представление и адекватно их отображающие), и
несколько стандартных примеров отношений и запросов на MiniKanren. Интерпрета-
тор демонстрирует ожидаемое поведение.

4.5. Формализация доказательства эквивалентности семантик


в Coq
Формализация доказательства корректности операционной семантики относитель-
но денотационной содержится в файле OpSemSoundness.v. Итоговая формулировка
выглядит следующим образом.
Lemma search_correctness :
∀ g n f t,
closed_goal_in_context (first_nats n) g →
op_sem ( State ( Leaf g empty_subst n)) t →
{| t , f |}) →
[| g , f |].
Формализация доказательства полноты операционной семантики относительно де-
нотационной содержится в файле OpSemCompleteness.v. Итоговая формулировка вы-
глядит следующим образом.
Lemma search_completeness
∀ g n f t,
consistent_goal g →
closed_goal_in_context (first_nats n) g →
op_sem ( State ( Leaf g empty_subst n)) t →
[| g , f |] →
∃ f’, {| t , f’ |} ∧ ∀ x, In x ( first_nats n) → gt_eq (f x) ( f’ x).

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

31
5. Прочие применения новой семантики
Помимо доказательства нашего основного результата — полноты поиска в языке
MiniKanren — определенная нами семантика может быть использована в качестве
инструмента для доказательства других полезных свойств исполнения программ в
языке. В данном разделе будут рассмотрены два примера таких применений. Резуль-
таты из данного раздела на данный момент не формализованы в Coq, однако они
не имеют принципиальной ценности сами по себе, в отличие от полноты поиска, и
используются нами только для демонстрации применимости семантики для доказа-
тельства различных свойств языка.

5.1. Корректность языковых преобразований


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

Лемма 12. Для любых целей g1 , g2 , g3 верно:

1. Jg1 ∨ g2 K = Jg2 ∨ g1 K

2. Jg1 ∧ g2 K = Jg2 ∧ g1 K

3. J(g1 ∨ g2 ) ∧ g3 K = J(g1 ∧ g3 ) ∨ (g2 ∧ g3 )K

4. J(g1 ∧ g2 ) ∨ g3 K = J(g1 ∨ g3 ) ∧ (g2 ∨ g3 )K

5. J(fresh x . g1 ) ∨ g2 K = Jfresh x . (g1 ∨ g2 )K, если x не встречается в g2

6. J(fresh x . g1 ) ∧ g2 K = Jfresh x . (g1 ∧ g2 )K, если x не встречается в g2

Доказательство. Тривиально по определению.

Стоит отметить, что таким образом мы гарантируем только совпадение результов


как множеств константных термов. Их представление в виде подстановок, а также
некоторые аспекты поведения программ могут отличаться. В частности, возможная
потеря завершимости программ при изменении порядка целей в конъюнкциях явля-
ется известной проблемой в MiniKanren [5].

32
5.2. Опровергающая полнота отношений
Помимо доказательства общих свойств языка MiniKanren, мы можем использовать
нашу семантику для доказательства свойcтв конкретных программ. К ним относят-
ся некоторые важные свойства, описанные в литературе. Примером такого свойства
является опровергающая полнота (refutational completeness [5]) отношений.
Опровергающая полнота утверждает, что при вызовах данного отношения поиск
никогда не зацикливается, когда ответы заканчиваются. В более строгом виде встре-
чаются две различные формулировки: более слабая — любой вызов, не дающий отве-
тов, завершается — и более сильная — любой вызов, дающий конечное число ответов,
завершается. Операционная семантика для поиска с чередования позволяет форма-
лизовать обе эти формулировки.

Определение 2. Отношение rk называется опровергающе полным в слабой форме,


если для любого набора аргументов t1 , . . . , tk , такого, что

• все переменные в нем принадлежат множеству {γ1 , . . . , γn }


⟨ ⟩
• на пути из состояния rk (t1 , . . . , tk ), ϵ, n нет помеченных переходов
⟨ ⟩
верно, что путь из состояния rk (t1 , . . . , tk ), ϵ, n приходит в терминальное состо-
яние.

Определение 3. Отношение rk называется опровергающе полным в сильной форме,


если для любого набора аргументов t1 , . . . , tk , такого, что

• все переменные в нем принадлежат множеству {γ1 , . . . , γn }


⟨ ⟩
• на пути из состояния rk (t1 , . . . , tk ), ϵ, n число помеченных переходов конечно
⟨ ⟩
верно, что путь из состояния rk (t1 , . . . , tk ), ϵ, n приходит в терминальное состо-
яние.

Для содержательных отношений добиться такого свойства довольно сложно. Даже


приведенное в разделе 1.1 отношение append не является опровергающе полным (ни в
одной из форм), что мы можем доказать в рамках нашей семантики. Этот факт был
неожиданным для нас и был обнаружен при попытке доказательсва обратного.

Лемма 13. Отношение append не является опровергающе полным в слабой форме.

Доказательство. Приводится контрпример вызова: append2 (γ1 , Cons2 (Nil0 , Nil0 ), γ1 ).


Чтобы показать бесконечность пути для данного вызова мы определяем множество
“плохих” состояний, включающее данный вызов, и показываем, что путь из любого
плохого состояние приходит к другому плохому состоянию, причем на пути между
ними всегда встречается ответ. Подробности в приложении A.3.

33
Более достижимым свойством является ограниченная версия опровергающей пол-
ноты, при которой аргументам отношения при вызове не разрешается иметь общих
переменных. Мы будем называть это свойство линейной опровергающей полнотой.

Определение 4. Отношение rk называется линейно опровергающе полным в слабой


форме, если для любого набора аргументов t1 , . . . , tk , такого, что

• все переменные в нем принадлежат множеству {γ1 , . . . , γn }

• для любых i ̸= j, F V (ti ) ∩ F V (tj ) = ∅


⟨ ⟩
• на пути из состояния rk (t1 , . . . , tk ), ϵ, n нет помеченных переходов
⟨ ⟩
верно, что путь из состояния rk (t1 , . . . , tk ), ϵ, n приходит в терминальное состо-
яние.

Определение линейной опровергающей полноты в сильной форме аналогично. Опре-


делению арифмитических отношений с таким свойством посвещена работа [24]. Там
это свойство доказывается достаточно сложными рассуждениями о множествах ре-
шений и противоречиях в ветвях поиска. Мы можем строго доказать это свойство (в
обеих формах) для append в рамках нашей семантики.

Лемма 14. Отношение append является линейно опровергающе полным в сильной


форме.

Доказательство. Индукция по суммарному размеру первого и третьего аргумента


при вызове отношения. Подробности в приложении A.4.

34
Заключение
В рамках данной работы были получены следующие результаты.

1. Определена операционная семантика языка MiniKanren, задающая поиск с че-


редованием, в виде системы переходов с пометками.

2. Доказана полнота поиска в языке MiniKanren в форме корректности и полноты


новой операционной семантики относительно денотационной.

3. Семантики и доказательства формализованы в среде Coq, из формализации из-


влечен корректный по построению интерпретатор языка.

4. Продемонстрированы применения семантики для доказательства полезных об-


щих свойств языка (корректности языковых преобразований) и свойств конкрет-
ных программ (опровергающей полноты).

Естественным направлением для дальнейшей работы кажется добавление в се-


мантику важнейших расширений языка, в первую очередь — неравенств (disequality
constraints [31]), которое позволит обосновать их корректность. Также существует
множество других полезных свойств языка, которые можно попробовать доказать
с использованием нашей семантики, например, критерии расходимости поиска или
корректность потенциальных оптимизаций.

35
Список литературы
[1] Baader Franz, Snyder Wayne. Handbook of Automated Reasoning / Ed. by
Alan Robinson, Andrei Voronkov. –– Amsterdam, The Netherlands, The Netherlands :
Elsevier Science Publishers B. V., 2001.

[2] Backtracking, Interleaving, and Terminating Monad Transformers: (Functional


Pearl) / Oleg Kiselyov, Chung-chieh Shan, Daniel P. Friedman, Amr Sabry //
SIGPLAN Not. –– 2005. –– . –– Vol. 40, no. 9. –– P. 192–203.

[3] Bertot Yves, Castéran Pierre. Interactive Theorem Proving and Program Development
- Coq’Art: The Calculus of Inductive Constructions. Texts in Theoretical Computer
Science. An EATCS Series. –– Springer, 2004.

[4] Bove Ana. Programming in Martin-Löf Type Theory: Unification – A non-


trivial Example // DEPARTMENT OF COMPUTER SCIENCE, CHALMERS
UNIVERSITY OF TECHNOLOGY. –– 1999. –– P. 22–42.

[5] Byrd William E. Relational Programming in miniKanren: Techniques, Applications,


and Implementations : Ph. D. thesis / William E. Byrd ; Indiana University. –– 2009. ––
September.

[6] Byrd William E., Friedman Daniel P. αkanren: A Fresh Name in Nominal Logic
Programming // Proceedings of the 2007 Annual Workshop on Scheme and Functional
Programming. –– 2007. –– P. 79–90.

[7] Byrd William E., Holk Eric, Friedman Daniel P. miniKanren, Live and Untagged:
Quine Generation via Relational Interpreters (Programming Pearl) // Proceedings of
the 2012 Annual Workshop on Scheme and Functional Programming. –– Scheme ’12. ––
New York, NY, USA : ACM, 2012. –– P. 8–29.

[8] Chlipala Adam. Certified Programming with Dependent Types: A Pragmatic


Introduction to the Coq Proof Assistant. –– The MIT Press, 2013.

[9] Debray Saumya K., Mishra Prateek. Denotational and operational semantics for
PROLOG // Formal Description of Programming Concepts - III: Proceedings of
the IFIP TC 2/WG 2.2 Working Conference on Formal Description of Programming
Concepts - III, Ebberup, Denmark, 25-28 August 1986. –– 1987. –– P. 245–274.

[10] Despeyroux Joëlle, Felty Amy P., Hirschowitz André. Higher-Order Abstract Syntax
in Coq // Typed Lambda Calculi and Applications, Second International Conference
on Typed Lambda Calculi and Applications, TLCA ’95, Edinburgh, UK, April 10-12,
1995, Proceedings. –– 1995. –– P. 124–138.

36
[11] Friedman Daniel P., Byrd William E., Kiselyov Oleg. The Reasoned Schemer. –– The
MIT Press, 2005.

[12] Hemann Jason, Friedman Daniel P. µKanren: A Minimal Functional Core for
Relational Programming // Proceedings of the 2013 Annual Workshop on Scheme
and Functional Programming. –– 2013.

[13] Jones Neil D., Mycroft Alan. Stepwise Development of Operational and Denotational
Semantics for Prolog // Proceedings of the 1984 International Symposium on Logic
Programming, Atlantic City, New Jersey, USA, February 6-9, 1984. –– 1984. –– P. 281–
288.

[14] Keller Robert M. Formal Verification of Parallel Programs // Commun. ACM. ––


1976. –– Vol. 19, no. 7. –– P. 371–384.

[15] Kothari Sunil, Caldwell James. A Machine Checked Model of Idempotent MGU
Axioms For Lists of Equational Constraints // Proceedings 24th International
Workshop on Unification, UNIF 2010, Edinburgh, United Kingdom, 14th July 2010. ––
2010. –– P. 24–38.

[16] Kriener Jael, King Andy. Semantics for Prolog with Cut - Revisited // Functional
and Logic Programming - 12th International Symposium, FLOPS 2014, Kanazawa,
Japan, June 4-6, 2014. Proceedings. –– 2014. –– P. 270–284.

[17] Kriener Jael, King Andy, Blazy Sandrine. Proofs you can believe in: proving
equivalences between Prolog semantics in Coq // 15th International Symposium
on Principles and Practice of Declarative Programming, PPDP ’13, Madrid, Spain,
September 16-18, 2013. –– 2013. –– P. 37–48.

[18] Kumar Ramana. Mechanising Aspects of miniKanren in HOL. –– Bachelor Thesis, The
Australian National University. –– 2010.

[19] Lloyd J. W. Foundations of Logic Programming. –– Berlin, Heidelberg : Springer-


Verlag, 1984.

[20] Lozov Petr, Vyatkin Andrei, Boulytchev Dmitry. Typed Relational Conversion //
Proceedings of the International Symposium on Trends in Functional Programming. ––
2017.

[21] Near Joseph P., Byrd William E., Friedman Daniel P. alpha-leanTAP: A
Declarative Theorem Prover for First-Order Classical Logic // Logic Programming,
24th International Conference, ICLP 2008, Udine, Italy, December 9-13 2008,
Proceedings. –– 2008. –– P. 238–252.

37
[22] Paulson Lawrence C. Verifying the Unification Algorithm in LCF // Sci. Comput.
Program. –– 1985. –– Vol. 5, no. 2. –– P. 143–169.

[23] Pfenning F., Elliott C. Higher-order Abstract Syntax // SIGPLAN Not. –– 1988. ––
. –– Vol. 23, no. 7. –– P. 199–208.

[24] Pure, Declarative, and Constructive Arithmetic Relations (Declarative Pearl) /


Oleg Kiselyov, William E. Byrd, Daniel P. Friedman, Chung-Chieh Shan //
Proceedings of the 9th International Conference on Functional and Logic
Programming. –– FLOPS’08. –– Berlin, Heidelberg : Springer-Verlag, 2008. –– P. 64–80.

[25] Ribeiro Rodrigo Geraldo, Camarão Carlos. A Mechanized Textbook Proof of a Type
Unification Algorithm // Formal Methods: Foundations and Applications - 18th
Brazilian Symposium, SBMF 2015, Belo Horizonte, Brazil, September 21-22, 2015,
Proceedings. –– 2015. –– P. 127–141.

[26] Rozplokhas Dmitri, Boulytchev Dmitri. Improving Refutational Completeness of


Relational Search via Divergence Test // Proceedings of the 20th International
Symposium on Principles and Practice of Declarative Programming. –– PPDP ’18. ––
New York, NY, USA : ACM, 2018. –– P. 18:1–18:13.

[27] A Shallow Scheme Embedding of Bottom-Avoiding Streams / William E. Byrd,


Daniel P. Friedman, Ramana Kumar, Joseph P. Near.

[28] A Small Embedding of Logic Programming with a Simple Complete Search /


Jason Hemann, Daniel P. Friedman, William E. Byrd, Matthew Might // SIGPLAN
Not. –– 2016. –– . –– Vol. 52, no. 2. –– P. 96–107.

[29] Swords Cameron, Friedman Daniel P. rKanren: Guided Search in miniKanren //


Proceedings of the 2013 Annual Workshop on Scheme and Functional Programming. ––
2013.

[30] A Unified Approach to Solving Seven Programming Problems (Functional Pearl) /


William E. Byrd, Michael Ballantyne, Gregory Rosenblatt, Matthew Might // Proc.
ACM Program. Lang. –– 2017. –– . –– Vol. 1, no. ICFP. –– P. 8:1–8:26.

[31] cKanren: miniKanren with Constraints / Claire E. Alvis, Jeremiah J. Willcock,


Kyle M. Carter et al. // Proceedings of the 2011 Annual Workshop on Scheme and
Functional Programming. –– 2011. –– .

38
A. Детальные доказательства приведенных
утверждений
В этом разделе будут приведены неочевидные детали для некоторых доказательств
из данной работы. При этом доказательства всех утверждений из данной работы,
кроме утверждений из раздела 5, верифицированы в Coq, и наиболее полные доказа-
тельства содержатся в формализации (см. раздел 4).

A.1. Доказательство леммы 1


Доказательство проводится структурной индукцией по цели g.
Во всех случаях, кроме введения свежей переменной, утверждение тривиально
следует из индукционных предположений.
Рассмотрим случай, в котором g = fresh y . g ′ . В этом случае в цели g ′ могут встре-
чаться синтаксические переменные x и y (случай, когда x не встречается в цели явля-
ется тривиальным, и его следует рассмотреть отдельно). По условию и определению
денотационной семантики мы имеем f′1 ∈ Jg ′ [α1 /x] [α3 /y]K, где α3 — свежая перемен-
ная, а f′1 — некоторая функция, которая может отличаться от f1 только в точке α3 .
При этом α3 не может быть равна α1 , но может быть равна α2 и нам в дальнейшем
иногда придется рассматривать эти случаи поочередно, так как они принципиально
различны. Мы должны предъявить некоторую свежую переменную α4 и означиваю-
щую функцию f′2 ∈ Jg ′ [α2 /x] [α4 /y]K, которая может отличаться от f2 только в точке
α4 .
Среди разных вариантов, которые мы рассмотрели, наиболее простым выбором
значения α4 оказалась как можно более свежая логическая переменная (не равная
всем остальным и не встречающаяся в цели). Далее нам нужно перейти от функции
f′1 к функции f′2 , дважды применив индукционное предположение (так как мы теперь
меняем местами две переменные одновременно). Для этого нам нужно аккуратно ме-
нять значение функции в подходящих точках, так чтобы они попарно удовлетворя-
ли условиям из формулировки леммы. Как оказалось, для этого придется заменить
значения функции во всех четырех доступных точках. Сначала мы строим проме-
жуточную функцию f′1.5 = f′1 [α3 ← f2 (α3 )][α4 ← f′1 (α3 )], которая должна оказаться в
промежуточной семантике Jg ′ [α1 /x] [α4 /y]K. Затем из нее строим итоговую функцию
f′2 = f′1.5 [α1 ← f2 (α1 )][α2 ← f′1.5 (α1 )]. При обоих переходах нужно аккуратно убедиться в
выполнении условий взаимосвязи функций, рассмотрев значения на всех переменных.

A.2. Доказательство леммы 11


Доказательство проводится индукцией по уровню ограниченной денотационной
семантики, а доказательство индукционного перехода — структурной индукцией по

39
цели.

• В случае унификации мы пользуемся тем фактом, что подстановка, унифици-


рующая два терма, существует тогда и только тогда, когда существует означи-
вающая функция, их приравнивающая (так как необходимо связывать только
переменные, встречающиеся в термах, которых конечное число). Дальше всё
следует из свойств наиболее общего унификатора.

• В случае дизъюнкции мы применяем лемму 4 к индукционным предположениям.

• В случае конъюнкции мы применяем лемму 5 к индукционным предположениям.


Заметим, что из-за этого случая простой индукции по определению денотацион-
ной семантики вместо наших двух вложенных индукций было бы недостаточно,
потому что здесь нам необходимо применить индукционные предположения к
разным означивающим функциям (возможно отличающихся на переменных с
индексами больше n).

• Случай введения свежей переменной является наименее тривиальным (как обыч-


но). У нас не получится просто воспользоваться индукционным предположени-
ем, потому что в нем говорится о семантике цели с произвольной подставленной
свежей переменной (из определения денотационной семантики), нам же необ-
ходимо связать семантики для цели в которую в качестве свежей переменной
подставляется именно первая неаллоцированная (из шага операционной семан-
тики). Для того, чтобы преодолеть это несоответствие, мы применяем лемму 1,
при этом теряя равенство значений функций на переменной γn+1 . Именно в этом
месте доказательство равенства семантик без ограничения доменов функций на
переменные, выделенные в начале, не сработало бы (как говорилось выше, тео-
рема в такой формулировке неверна).

• В случае вызова отношения мы просто пользуемся индукционным предположе-


нием для предыдущего уровня.

A.3. Доказательство леммы 13


Чтобы показать бесконечность пути для некоторого вызова мы построим множе-
ство “плохих” нетерминальных состояний S, включающее этот вызов, для которого
будет верно, что путь из любого состояния из S вновь приходит в состояние из S
и, таким образом, никогда не кончается. Формально условие на S выглядит следую-
щим образом (здесь ⇝ обозначнает существование пути из непомеченных переходов
произвольной длины).

∀ŝ ∈ S, ∃s1 , s2 , ŝ′ : ŝ ⇝ (s1 ⊕ s2 ) ∧ s1 ⇝ ♢ ∧ s2 ⇝ ŝ′ ∧ ŝ′ ∈ S

40
Для множества с этим свойством легко показать, что на пути из любого состояния
из этого множества нет помеченных переходов, при этом путь никогда не приходит в
терминальное состояние. Именно это и нужно, чтобы найти контрпример для опро-
вергающей полноты.
В качестве множества S возьмем множество согласованных состояний

{⟨append(α, Cons(Nil, Nil), α), σ, n⟩ | α ̸∈ Dom (σ)}.

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


такое определение подходит под условие: вычислении диъюнкции в append разделя-
ется на две ветви, первая из которых завершается без ответов, а вторая превращается
в рекурсивный вызов с некоторой новой свободной переменной. Поэтому любой вы-
зов, содержащийся в этом множестве, например ⟨append(γ1 , Cons(Nil, Nil), γ1 ), ϵ, 2⟩,
будет контрпримером для опровергающей полноты.

A.4. Доказательство леммы 14


Воспользуемся индукцией по суммарной высоте термов, являющихся первым и
третьим аргументом вызова (для удобства будем считать, что высота равна нулю для
переменной и максимальной высоте аргумента конструктора (положеной нулю, если
аргументов нет), увеличенной на 1, для конструкторов).

• В случае, когда оба эти аргумента являются переменными, которые по условию


нелинейности не равны, мы можем показать, что число ответов на пути не яв-
ляется конечным. Для этого можно воспользоваться точно таким же методом,
каким мы пользовались для доказательства предыдущей леммы и построить
множество нетерминальных состояний, каждое из которых переходит путем в
другое (скомпонованное с некоторой добавкой с помощью ⊕). Только в нашем
текущем случае переменные-аргументы в вызовах из множества будут неравны-
ми, поэтому на пути из состояния s1 в терминальное всегда будет ровно один
ответ, из чего легко следует наличие бесконечного числа ответов на любом пути
из множества.

• В случае, когда один из этих аргументов является конструктором, отличным от


конструктора Cons2 , можно легко понять, что путь конечен (вычисление второй
ветви дизъюнкции обрывается до рекурсивного вызова).

• Наконец, в случае, когда один из этих аргументов является конструктором Cons2 ,


а второй является конструктором Cons2 или переменной, мы можем пройти по
пути до рекурсивного вызова с аргументами с меньшей суммарной высотой (как
минимум один конструктор Cons2 снимается) и воспользоваться для него ин-
дукционным предположением. Из того, что вычисление рекурсивного вызова

41
соответствует опровергающей полноте, следует, что это же верно и для исход-
ного вызова: если число помеченых переходов для исходного вызова конечно, то
и число помеченных переходов для рекурсивного вызова кончено, а значит он
завершается, следовательно завершается и исходный (ведь вычисление второй
ветви дизъюнкции очевидно конечно).

42

Вам также может понравиться