Научная статья на тему 'Преобразование типизированных функций в реляционную форму'

Преобразование типизированных функций в реляционную форму Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
144
23
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
ФУНКЦИОНАЛЬНОЕ ПРОГРАММИРОВАНИЕ / FUNCTIONAL PROGRAMMING / РЕЛЯЦИОННОЕ ПРОГРАММИРОВАНИЕ / RELATIONAL PROGRAMMING / ГЕНЕРАЦИЯ ПРОГРАММ / CONVERSION OF PROGRAMS

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Лозов П.А., Булычев Д.Ю.

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

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Conversion Typed Functions into Relational Form

Relational programming is an approach that allows you to execute programs in different "directions" to get different behaviors from one relational specification. The direct development of relational programs is a complex task, requiring the developer to have certain skills. However, in many cases, the required relational program can be obtained from a certain functional program automatically. In this paper, the problem of automatic conversion of functional programs into relational ones is considered. The main contribution of the paper is the method of converting typed functions into a relational form, as well as proving its static and dynamic correctness. This method can be applied to typed programs of a general kind. To describe these programs, a compact ML-like language (a subset of OCaml) is used, equipped with a Hindley-Milner type system with let-polymorphism Also, the paper discusses the limitations of the proposed method, presents an implementation for a subset of the OCaml language, and evaluates the method on a number of realistic examples.

Текст научной работы на тему «Преобразование типизированных функций в реляционную форму»

Преобразование типизированных функций в реляционную форму

П.А. Лозов <lozov.peter@gmail.com> Д.Ю. Булычев <dboulytchev@math.spbu.ru> Санкт-Петербургский государственный университет, 198504, Россия, Санкт-Петербург, Университетский пр., д. 28

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

Ключевые слова: функциональное программирование; реляционное программирование; генерация программ.

DOI: 10.15514/ISPRAS-2018-30(2)-3

Для цитирования: Лозов П.А., Булычев Д.Ю. Преобразование типизированных функций в реляционную форму. Труды ИСП РАН, том 30, вып. 2, 2018 г., стр. 45-64. DOI: 10.15514/ISPRAS-2018-30(2)-3

1. Введение

Реляционное программирование основано на построении программы в виде набора отношений [1]. Реляционная программа может быть исполнена в различных "направлениях", то есть независимо от того, какие из аргументов отношения известны, а какие необходимо найти. Это делает возможным, например, вычисление обратных функций. Хотя многие языки логического программирования, такие как Prolog, Mercury [2] или Curry [3], позволяют использовать некоторые реляционные эффекты, был создан miniKanren [4] -специальный язык для реляционного программирования. Изначально данный язык был небольшим предметно-ориентированным расширением языка Scheme/Racket, его минимальная реализация [5] содержала менее ста строк кода. Впоследствии miniKanren нашел применение, будучи встроенным во

многие языки программирования, среди которых Haskell, Standard ML и OCaml.

Непосредственная разработка реляционных программ является сложной задачей, требующей от разработчика определенных навыков. Однако во многих случаях требуемая реляционная программа может быть получена из некоторой функциональной программы автоматически. Таким образом, появляется задача автоматического преобразования функциональных программ в реляционные. Отметим, что единственный существующий подобный метод Unnesting [6] не полон и не был реализован. Основным вкладом данной статьи является метод реляционного преобразования, который может быть применён к типизированным программам общего вида. Для описания этих программ используется компактный ML-подобный язык (является подмножеством OCaml), оснащенный системой типов Хиндли-Милнера с let-полиморфизмом [7]. Оставшаяся часть статьи организована следующим образом. В разд. 2 приведен краткий обзор схожих работ. Разд. 3 посвящена реляционному программированию и языку miniKanren. Разд. 4 содержит описание синтаксиса, правил вывода типов и операционной семантики как для ML-подобного языка, так и для его реляционного расширения. В разд. 5 представлены формальные правила для реляционного преобразования, а также доказательства сохранения типизации и семантики. В разд. 6 представлены реализация описанного метода и несколько примеров результата преобразования.

2. Схожие работы

Взаимодействие декларативных языков программирования (а реляционное программирование является декларативным) с языками более прагматичными (например, Java или OCaml) является естественной задачей. Успешное решение это задачи позволяет совместить выразительность декларативного языка с оптимизированностью и обширной функциональностью императивного или функционального языка. В качестве примера можно упомянуть работы [8; 9], где для описания ограничений на метамодель REAL [10] используется декларативный язык OCL (Object Constraint Language), который позволил кратко и понятно описать необходимые ограничения, однако оказался непригоден для работы с реальными данными. Поэтому ограничения на языке OCL были преобразованы в скрипты, написанные на императивном языке JavaScript.

В контексте данной работы наиболее показательным декларативным языком является Prolog. Во-первых, между ним и языком miniKanren много общего; во-вторых, задача взаимодействия этого языка с другими хорошо исследована. Прежде всего, для языка Prolog существуют методы компиляции в более низкоуровневые языки, например в язык С [11; 12]. В [14] представлен метод преобразования из Prolog в Java, позволяющий увеличить эффективность 46

исполнения декларативной программы, включающий метод декомпиляции Java Bitecode в Prolog [14]. В [15] представлен подход, встраивающий Prolog в Java, в [16] аналогично, Prolog встроен в C#. Тем не менее, не смотря на все эти подходы, проблема взаимодействия декларативных и императивных языков программирования далека до полного разрешения. В случае взаимодействия функционального языка с реляционным можно выделить обратную задачу: преобразование функциональных программ в реляционную форму. Решение данной задачи позволит, с одной стороны, использовать привычный язык программирования (в нашем случае OCaml) для описания функций, а, с другой стороны, исполнять функциональные программы реляционно (в частности, моделировать вычисление обратных функций). Для данной задачи было предложено решение Unnesting [6], которое, однако, рассматривает только случай нетипизированных программ и работает для специальных примеров. Более того, оно не был реализовано.

3. Реляционное программирование и язык miniKanren

Основной особенностью языка miniKanren [1; 6] является отсутствие различий между аргументами и результатом, что позволяет исполнять программы в различных "направлениях". Такой подход имеет практическое значение: некоторые задачи формулируются гораздо проще, если рассматривать их как запросы к реляционной программе. Существует целый ряд примеров, подтверждающих это наблюдение. К примеру, задача вывода типов для просто-типизированного лямбда-исчисления или задача о выявлении населенности какого-либо типа могут быть сформулированы в виде запросов к более простой в реализации реляционной программе проверки корректности типов. Другим примером может послужить задача генерации "квайнов" [17] -программ, результатом исполнения которых являются они сами. Данная задача может быть представлена как запрос к реляционному интерпретатору, также более простому в сравнении с изначальной задачей. Наконец, генератор всех перестановок элементов данного списка выразим в виде запроса к реляционной сортировке списка [18]. В контексте данной статьи будет использоваться конкретная реализация языка miniKanren - DSL на основе Objective Caml4, называемый OCanren [19]. Данный язык соответствует оригинальной реализации miniKanren [5], но OCanren дополнен конструкцией "Disequality constraint" [20].

Основной синтаксической единицей как реляционного языка miniKanren, так и языка OCanren является цель (goal). Для построения элементарных целей используется синтаксическая унификация (t1 = t2) и обратная к ней операция disequality constraint (z, ^ t2). Для построения сложных целей используются дизъюнкция (g1 V g2), конъюнкция (g1 Л g2) и операция введения "свежей переменной" (fresh (х) g). Результат выполнения реляционной программы представляется в виде потока данных, из которого можно запрашивать

Lozov P., Boulytchev D. Conversion Typed Functions into Relational Form. Trudy ISP RAN/Proc. ISP RAS, vol. 30, issue 2, 2018, pp. 45-64

решения. В качестве примера рассмотрим реляционную программу сложения чисел Пеано:

1 type num = 0 | S of num

Интерпретировать отношение "add a b c" нужно как следующее утверждение: "сумма a и b равняется c". Действительно, в случае, когда a равняется нулю, b в точности совпадает с c (строка 4). Также a можно представить в виде S а' (строка 7) - для этого понадобится "свежая" переменная а' (строка 5). Также понадобится дополнительная переменная с' для обозначения суммы а' и b, что выражается в виде "add a' b с'" (строка 8). Остается указать, что c должно быть на единицу больше, чем с' (строка 9).

Рис. 1. Примеры использования отношения add Fig. 1. Examples of using relation add

Рассмотрим несколько различных целей, построенных с помощью отношения add, и изображенных на рис. 1. В каждом примере отношение принимает следующие возможные аргументы: выражения-константы, "свежие" переменные, внедренные с помощью конструкции fresh. Прежде всего отношение add можно использовать для сложения двух чисел. Для этого в качестве аргументов ему необходимо передать эти числа и "свежую"

Лозов П.А., Булычев Д.Ю. Преобразование типизированных функций в реляционную форму. Труды ИСП РАН, том 30, вып. 2, 2018 г., стр. 45-64

переменную. В этом случае результатом вычисления цели будет поток, содержащий сумму этих чисел. На рис. 1a приведен пример суммы двух единиц. Помимо сложения с помощью данного отношения можно произвести вычитание, передав уменьшаемое в качестве третьего аргумента, вычитаемое в качестве первого аргумента и "свежую" переменную, олицетворяющую разность, в качестве второго аргумента. На рис. 1b приведен пример разности чисел 3 и 2. Также отношение add позволяет сгенерировать все пары слагаемых для фиксированной суммы. Для достижения этой цели передадим отношению две "свежих" переменных и число. Полученный после вычисления цели поток будет содержать все пары корректных слагаемых. На рис. 1c приведён пример генерации слагаемых для суммы, равной двум. Наконец, данное отношение позволяет выявить некорректные аргументы, ведь получение в качестве результата вычисления цели пустого потока сигнализирует об отсутствии правильных решений. На рис. 1d приведен пример попытки прибавить к трем неотрицательное число и получить два.

4. Входной язык и его реляционное расширение

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

Рис. 2. Синтаксис входного языка Fig. 2. Syntax of input language

Синтаксис исходного функционального языка показан на рис. 2. Данный язык является расширением лямбда-исчисления конструкторами с фиксированной размерностью С, двумя предопределенными конструкторами true и false, операцией синтаксического сравнения "=", шаблонами p и конструкциями

сопоставления с образцом, а также выражениями для рекурсивных/нерекурсивных let-ссылок.

При использовании сопоставления с образцом доступны только шаблоны вида Cn(x1, ... , xn). Данное ограничение несущественно, так как шаблоны общего вида выразимы с помощью описанных выше. Также запрещен специальный шаблон wildcard (обозн. "_"), позволяющий игнорировать сопоставляемую ему часть выражения.

Данный язык оснащен системой вывода типов Хиндли-Милнера, правила которой описаны в прил. A. Система вывода типов поддерживает типовые преременные, функциональные типы, а также набор неявно определенных алгебраических типов данных Tk, причем каждый конструктор Cn принадлежит ровно одному типу, и конструкторы true и false принадлежат выделенному алгебраическому типу bool.

Семантика данного языка представлена в прил. B и является системой переходов между состояниями. Отношение перехода

<5,е> ^ <S',e'>

описывает один шаг вычисления выражения e в стеке контекстов S, после которого будет получено новое выражение e' с обновленным стеком контекстов S'. Контекст представляет из себя выражение с уникальной дырой; неформально говоря, стек контестов описывает путь вычисления выражения от внешнего уровня до места, где в текущий момент остановлено вычисление. Для контекста C и выражения e обозначим C[e] - полное выражение без дыр, полученное путем подстановки e вместо уникальной дыры в C. Для состояния ( Cj : ... : Cn, e) полным выражением является Cn[...[C1[e]]...], которое является промежуточным результатом вычисления. Наконец, выражение e вычисляется к результирующему значению v если

<е,е> (e,v>,

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

—»

£ += fresh (х) е

ei = е2 ei ф е2 ei V е2 _е1 Л е2

Рис. 3. Синтаксис реляционного расширения Fig. 3. Syntax of relational extension

Реляционное расширение добавляет пять стандартных конструкций языка miniKanren для построения целей, синтаксис которых отображен на рис. 3. Вследствие добавления конструкций miniKantren к конструкциям функционального языка, становится возможным построение всевозможных

смешанных выражений, к примеру, конъюнкция (Xx.x Л Xy.y). Для устранения подобных некорректных выражений была расширена система типов для исходного языка, что описано в прил. C. Фактический, данный подход следует реализации языка OCaml, где строгая система типов позволяет исключить большинство некорректных программ во время компиляции. Также система типов была дополнена специальным типом G, олицетворяющим результат вычисления отношения.

Семантика расширенного языка представлена в прил. D. Прежде всего, было расширено состояние: помимо стека контекстов и текущего выражения состояние теперь содержит множество использованных семантических переменных Е и реляционное состояние с. Семантические переменные вводятся и заменяют синтаксические переменные после каждого исполнения конструкции fresh. Реляционное состояние используется при исполнении унификации и desequality constraint. Все существующие правила исходного языка дополняются множеством семантических переменных и реляционным состоянием, но не используют их.

5. Преобразование функциональных программ в реляционную форму

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

• тип любого конструктора должен содержать либо типовые переменные, либо типовые константы;

• конструкторы и полиморфная операция сравнения могут быть применены только к значениям первого порядка;

• все match - выражения должны быть первого порядка.

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

match е with, {pi —J- е^} Ах.match е with {pi —» ei

где x - это вектор новых переменных, отсутствующих в выражениях e, e, и p. Отметим, что реализация, описанная в разделе 6, исполняет это расширение для match-выражений, если оно является выражением высшего порядка. Это единственный случай, когда для преобразования используется тип функциональной программы и ^-расширение.

Основная идея преобразования может быть проиллюстрирована на уровне типов: выражение типа t в исходном языке будет преобразовано в выражение типа [t]' в реляционном расширении языка, где преобразование [[■]' определяется следующим образом:

Другими словами, выражение первого порядка будет преобразовано в одноместную функцию, возвращающую значения типа G. Неформальная семантика данной функции состоит в том, чтобы сопоставить аргументу исходное значение. Например, константа Nil будет преобразована в функцию (X q . q = Nil).

Теперь рассмотрим преобразование выражений, обозначаемое [■F. Данное преобразование определеяется рекуррентно набором правил вида

[А]с = В,

где A - исходная функциональная программа, B - реляционная программа, являющаяся результатом преобразования. Ниже представлены правила для всех конструкций входного функционального языка.

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

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

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

A q. fresh {q(,) (1еГ Qe) А

V,: ((fresh ()

(qe=C^(q !,...,<)) A (A xl.-.x^-laf) (=q{) ... q

))

Правило для преобразования сопоставления с образцом работает аналогичным образом. Во-первых, скрутини (разбираемое значение e) должно быть выражением первого порядка (так как оно сопоставляется конструкторам). Создадим "свежую" переменную qe и свяжем её со значением скрутини так же, как и в предыдущем случае. Далее, для каждой ветки создадим несколько "свежих" переменных qij (по одной для каждой переменной в образце данной ветки) и выразим сопоставление образца с помощью оператора дизъюнкции, используя эти переменные и соответствующий конструктор. Наконец, тело ветки ei - это выражение со свободными переменными, соответствующими тем, что указаны в образце. Поэтому преобразуем выражение ei и окружим результат абстракциями, замыкающими все эти переменные и получим функцию. Теперь необходимо связать q^ со свободными переменными из образца. Для этого применим описанную выше функцию к функциям, возвращающим цель (= qj). В конечном итоге получим функцию, возвращающую цель, которую применим ко внешней переменной q, олицетворяющей результат исходного сопоставления с образцом.

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

53

match е with

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

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

Для подтверждения корректности преобразования были сформули-

рованы и доказаны следующие теоремы.

Теорема 1. (Статическая корректность). Если выражение e имеет тип t в исходном языке, тогда [е]с c имеет тип [t]' в реляционном расширении. Другими словами, преобразование в реляционную форму переводит правильно типизированные программы в правильно типизированные. Доказано с помощью структурной индукции.

Теорема 2. (Частичная семантическая корректность). Если выражение первого порядка e имеет тип t и e вычисляется до некоторого значения v

/ func \

(е —> v), тогда fresh (x) (Це]е x) вычисляется до подстановки в, и в(ё) = v,

где S семантическая переменная, ассоциированная с x на первом шаге вычисления. Доказано с помощью симуляции.

6. Апробация

Описанное в предыдущем разделе метод преобразования функциональных программ в реляционные был реализован на языке OCaml. В качестве входного языка используется подмножество OCaml, описанное в разделе 4. При реализации были выявлены две проблемы. Во-первых, результат преобразования содержит множество X-абстракций, многие из которых могут быть применены немедленно. Для их устранения был добавлен дополнительный опциональный проход по абстрактному синтаксическому дереву преобразованной программы, который выполняет p-редукцию везде, где это возможно. Данная оптимизация значительно улучшает качество конвертируемых программ как с точки зрения читаемости, так и по производительности. Далее, в нашей первоначальной реализации слишком много значений преобразовывались в функции и, как результат, во время исполнения их тела вычислялись несколько раз с существенным ухудшением производительности. Нами была улучшена реализация путем внедрения в результирующую реляционную программу принудительного вычисления

аргументов первого порядка для каждого содержащегося в программе вызова функции.

В качестве первого примера преобразования рассмотрим реализацию функции конкатенации для списков (см. рис. 4a). Результат преобразования (рис. 4b) несколько отличается от классического реализации реляционного отношения конкатенации списков. Основное различие происходит от функционализации примитивных значений: в то время как обычные append0 работает со значениями-списками, преобразованный вариант использует функции, возвращающие цель. Таким образом, классическая append0 для аргументов x, y и q может быть выражено с помощью преобразованного в качестве append0 (= x) (= y) q.

Далее мы продемонстрируем возможность выполнимости реляционных форм в различных направлениях на следующих примерах:

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

• алгоритм Хиндли-Милнера [7] для вывода наиболее общего типа.

val append : a list —^ a. list a list

let rec append = A a.A b. match a with | Mil > b

| Cons (h, t)

Cons (h, append t b)

(a)

val append" : (a Hist —> 0) — (a llist ->©)->• a llist >© let rec append0 a b ql = fresh (q2) (a q2) A

(((q2 = Nil) A (b ql)) || (fresh (q3 q4)

(q2 S Cons (q3, q4)) A (fresh (q6 q7) (q6 = q3) A (ql = Cons (q6, q7)) A (append0 (= q4) b q7)))) (b)

Рис. 4. Пример преобразования функции в отношение Pic. 4. Example of relational conversion

6.1 Интерпретатор высшего порядка для лямбда-исчисления

Как было сказано во введении, одним из применений языка miniKanren является разработка реляционных интерпретаторов [6; 18; 21]. Отличительной особенностью данного интерпретатора является его аргумент высшего порядка, который используется для поиска подвыражения, к которому применима бета-редукция. Таким образом, в зависимости от этого аргумента

Lozov P., Boulytchev D. Conversion Typed Functions into Relational Form. Trudy ISP RAN/Proc. ISP RAS, vol. 30, issue 2, 2018, pp. 45-64

можно получить интерпретатор с различными стратегиями вычисления: call-by-name, call-by-value, нормальный порядок редуциии и др. Реализованный функциональный интерпретатор и функции редукции имеют следующую сигнатуру:

где term - выражение лямбда-исчисления в нотации Де Брюэна; split -пара из выражения и контекста. Функция высшего порядка eval принимает в качестве первого аргумента функцию, определяющую порядок редукции, в качестве второго аргумента - выражение, которое необходимо редуцировать. После преобразования будут получены отношения со следующими сигнатурами:

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

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

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

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

eval" call_by_namea (= q) (:Л О') [

Лозов П.А., Булычев Д.Ю. Преобразование типизированных функций в реляционную форму. Труды ИСП РАН, том 30, вып. 2, 2018 г., стр. 45-64

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

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

6.2 Вывод типов Хиндли-Милнера

Данный алгоритм [22] по лямбда-выражению вычисляет наиболее общий тип этого выражения.

Функция type_inference, являющаяся реализацией алгоритма вывода типов и отношение type_inferenceo, являющееся результатом преобразования type_inference, имеют следующие типы:

val type_inference" : (term —» 0) —¥ typ —¥ ©

Полученное с помощью реляционного преобразования отношение можно использовать для вычисления типа выражения.

Данный запрос для заданного выражения порождает корректный тип. Также реляционную форму type_inferenceo можно использовать для решения проблемы населенности типа.

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

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

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

7. Выводы

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

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

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

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

[1]. Friedman D. P., E.Byrd W., Kiselyov O. The Reasoned Schemer. MIT Press, 2005.

[2]. Язык Mercury. URL: https://mercurylang.org (дата обращения 09.04.2018).

[3]. Язык Curry. URL: http://www-ps.informatik.uni-kiel.de/currywiki (дата обращения 09.04.2018).

[4]. Язык miniKanren. URL: http://minikanren.org (дата обращения 09.04.2018).

[5]. Hemann J., Friedman D. P. ^Kanren: A Minimal Core for Relational Programming. Workshop on Scheme and Functional Programming, 2013.

[6]. Byrd W. E. Relational Programming in miniKanren: Techniques, Applications, and Implementations. Ph.D. thesis, Indiana University, Bloomington, 2009.

[7]. Pierce B. Types and Programming Languages. MIT Press, 2002.

[8]. Кознов Д. В. Методология и инструментарий предметно-ориентированного моделирования. Диссертация на соискание учёной степени доктора технических наук, СПбГУ, 2016.

[9]. Ольхович Л., Кознов Д. Метод автоматической валидации UML-спецификаций на основе OCL. Программирование, 2003, том 29, № 6, стр. 44-50.

[10]. Терехов А.Н., Романовский К.Ю., Кознов Д.В., Долгов П.С., Иванов А.Н.. RTST++: методология и CASE-средство для разработки информационных систем и программного обеспечения для систем реального времени. Программирование, 1999, том 25, № 5.

[11]. Codognet P., Diaz D. WAMCC: Compiling Prolog to C. The MIT Press, 1995, pp. 317331.

[12]. Henderson F., Somogyi Z. Compiling mercury to high-level C code. In Computational Complexity, 2002, pp. 197-212.

[13]. Banbara M., Tamura N., Inoue K. Prolog Cafe: A prolog to Java translator system. Lecture Notes in Computer Science, vol. 4369, 2006, pp. 1-11.

[14]. G'omez-Zamalloa M., Albert E., Puebla G. Decompilation of Java bytecode to Prolog by partial evaluation. Information and Software Technology, 2009, vol. 51, № 10, pp. 1409-1427.

[15]. Calejo M. InterProlog: Towards a Declarative Embedding of Logic Programming in Java. JELIA 2004: Logics in Artificial Intelligence, pp. 714-717.

[16]. J. Cook J. P#: A concurrent Prolog for the .NET framework. Software Practice and Experience, vol. 34. № 9, 2004, pp. 815-845.

[17]. Byrd W. E., Holk E., Friedman D. P. miniKanren, Live and Untagged: Quine Generation via Relational Interpreters (Programming Pearl), Workshop on Scheme and Functional Programming, 2012.

[18]. Kosarev D., Boulytchev D. Typed Embedding of a Relational Language in OCaml. ACM SIGPLAN Workshop on ML, 2016.

[19]. Язык OCanren. URL: http://github.com/dboulytchev/ocanren (дата обращения 09.04.2018).

[20]. Alvis C. E., Willcock J. J., Byrd W. E. cKanren: miniKanren with Constraints, Workshop on Scheme and Functional Programming, 2011.

[21]. Byrd W. E., Ballantyne M., Rosenblatt G., Might M. A Unified Approach to Solving Seven Programming Problems (Functional Pearl). Proc. ACM Program. Lang, 2017, vol. 1, ICFP, pp. 8:1-8:26.

[22]. Barendregt H. Lambda Calculi with Types. Handbook of Logic in Computer Science, Volume II, Oxford University Press, 1993.

Приложения

А. Правила типизации для входного языка

Типы:

X — а, /?, . . . (типовые переменные)

V = Ьоо1. Т",... (конструкторы типов данных)

Т = (типы)

5 = ЧаЛ (схемы типов)

Правила типизации:

Г I- true, false : bool [boolt]

Г Ь ei : ty

[Co

Г h / : ij t2 Г h e : ii Г F / e : f2

Г h Cn(ei,... ,e„) : tc

■ t.

[APPJ]

Г h ei : ii Г, x : Vci.ii e2 : t Г h let x — e\ in : t

Г, / : <i h Ax.ej : ti Г. / : Va.ii he2:f

Г h ei : t Г h e2 : t Г b ei = e2 : bool

Г,х; Va.thx : t[a < ('] I • ••' : 'i / ■'•

Г h Ax./ : fi ¿2

, a = i,v(i1)\i,v(r) , a = FV(h)\FV(r)

Г h let rec / = Xx.ei in e-> : t

Г h e : tc r.x'j : if',... ,x\. : (- : t Г 1- match e with {Cf' (х117..., х'к,)-У ei} : f

[EqTj

[vart]

[abs'j'] [Lett] [LetREC?]

[MATCHT]

Lozov P., Boulytchev D. Conversion Typed Functions into Relational Form. Trudy ISP RAN/Proc. ISP RAS, vol. 30, issue 2, 2018, pp. 45-64

B. Семантика входного языка

(S, ß) (стек контекстов, выражение)J^ (б, в) (начала сото^ (с, V^ (финальное состояг(ис)

C. Правила типизации для реляционного расширения

Типы:

£, = Ot° | (Tn(J,\) . . . , (логические переменные)

т += ©

Правила типизации:

Г, х : I b е : «5

Г Ь fresh (ж) е : ©

Г h 61 :1 Г I- е2 : I Г h fi = е2 : © Г h ei : Я Г h е2 : © Г h Ё! Де2 : ©

[unify т1]

[CONJUNCTION^]

Г I- в! : I Г h e2 :1

Г b ф е2 : © Г h ei : © Г Ь с2 : © Г h е! V е2 : ©

[РпБЭПт]

[DlSEqUALiTYj]

[Disjunction^]

D. Семантика для реляционного расширения

Семантические переменные:

^ 6=SbS2,...

Е, 2 ■ ■ ■ CI 2 (множества выделенных семантических переменных) (£ ,3) i— new Е, S' — S U {з }. s ^ E (выделение новой семантической переменной)

Значения:

У += success | s

Контексты:

C+=D = e|t»sD|D^e|«?Sa|aAe|eAD

Состояния:

(Е, S. е, (j) (мн-во семмантических переменных, стек контекстов, выражение, логическое состояние)

{0, С, в, f.) (начальное сотояпке)

Переходы:

(Е, S, fresh (ж) е, сг) (Е'. 5, ф а], и), {E',s> new Е [fresh

{£, S, ei = e2, cr) —» {£, О s Ё2 '■ S, ei, c) [UNIFYL

(E, S,v = e,o)-~* (E, v = □ : S, e, cr) [unifyr

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

{£, S, Hi = V2, a) •w (E, 5. success, ст'), unify (с, Ui, w2) = и' [UNIFY

{£, 5, ej ^ ej, cr) {£, □ ^ e2 : <S, ei, a) [disEql

(E, 5, v ф. e, cr) {£, ^ □ : S, e, a) [disEqR

{£, S, v\ ф V2, cr) {£, 5. success, a'), diseq(<7, tij, ti2) = a' [disEq

(E, 5, V e2, a) •** (E, ,S, ei, cr) [DISJL

(E, 5, V e2, с) (E, S, 62, cr} [disjr

(E, S, ei Л e2l cr) {£, □ A e2 : <S, ei, u) [comStartl

{£, 5, ei Л e2, cr) {£, ej Л □ : S, e2, (7) [conjStabtR

{E, S, success Л e, cr) (E, 5, e, (7) [conjL

{E, S, e Л success, a) {£, <S, e, cr) [conjR

Conversion Typed Functions into Relational Form

P. Lozov <lozov.peter@gmail.com> D. Boulytchev <dboulytchev@math.spbu.ru> St. Petersburg State University, Universitetski pr., 28, 198504 St. Petersburg, Russia

Abstract. Relational programming is an approach that allows you to execute programs in different "directions" to get different behaviors from one relational specification. The direct development of relational programs is a complex task, requiring the developer to have certain skills. However, in many cases, the required relational program can be obtained from a certain functional program automatically. In this paper, the problem of automatic conversion

of functional programs into relational ones is considered. The main contribution of the paper is the method of converting typed functions into a relational form, as well as proving its static and dynamic correctness. This method can be applied to typed programs of a general kind. To describe these programs, a compact ML-like language (a subset of OCaml) is used, equipped with a Hindley-Milner type system with let-polymorphism Also, the paper discusses the limitations of the proposed method, presents an implementation for a subset of the OCaml language, and evaluates the method on a number of realistic examples.

Keywords: functional programming; relational programming; conversion of programs.

DOI: 10.15514/ISPRAS-2018-30(2)-3

For citation: Lozov P., Boulytchev D. Conversion Typed Functions into Relational Form. Trudy ISP RAN/Proc. ISP RAS, vol. 30, issue 2, 2018, pp. 45-64 (in Russian). DOI: 10.15514/ISPRAS-2018-30(2)-3

References

[1]. Friedman D. P., E.Byrd W., Kiselyov O. The Reasoned Schemer. MIT Press, 2005.

[2]. Mercury Language. URL: https://mercurylang.org (accessed 09.04.2018).

[3]. Curry Language. URL: http://www-ps.informatik.uni-kiel.de/currywiki (дата обращения 09.04.2018).

[4]. miniKanren Language. URL: http://minikanren.org (accessed 09.04.2018).

[5]. Hemann J., Friedman D. P. ^Kanren: A Minimal Core for Relational Programming. Workshop on Scheme and Functional Programming, 2013.

[6]. Byrd W. E. Relational Programming in miniKanren: Techniques, Applications, and Implementations. Ph.D. thesis, Indiana University, Bloomington, 2009.

[7]. Pierce B. Types and Programming Languages. MIT Press, 2002.

[8]. Koznov D. V. Methodology and tools for object-oriented modeling. PhD Thesis, SPBU, 2016 (in Russian).

[9]. Ol'khovich L., Koznov D.V. Ocl-based Automated Validation Method For Uml Specifications. Programming and Computer Software, 2003, vol. 29, № 6, pp. 323-327. DOI: 10.1023/B:PACS.0000004132.42846.11.

[10]. Terekhov A. N., Romanovskii K. Yu., Koznov D. V., Dolgov P. S., Ivanov A. N. RTST++: Methodology and a CASE Tool for the Development of Information Systems and Software For Real-Time Systems. Programming and Computer Software, 1999, vol. 25, № 5, pp. 276-281.

[11]. Codognet P., Diaz D. WAMCC: Compiling Prolog to C. The MIT Press, 1995, pp. 317331.

[12]. Henderson F., Somogyi Z. Compiling mercury to high-level C code. In Computational Complexity, 2002, pp. 197-212.

[13]. Banbara M., Tamura N., Inoue K. Prolog Cafe: A prolog to Java translator system. Lecture Notes in Computer Science, vol. 4369, 2006, pp. 1-11.

[14]. G'omez-Zamalloa M., Albert E., Puebla G. Decompilation of Java bytecode to Prolog by partial evaluation. Information and Software Technology, 2009, vol. 51, № 10, pp. 1409-1427.

[15]. Calejo M. InterProlog: Towards a Declarative Embedding of Logic Programming in Java. JELIA 2004: Logics in Artificial Intelligence, pp. 714-717.

[16]. J. Cook J. P#: A concurrent Prolog for the .NET framework. Software Practice and Experience, vol. 34. № 9, 2004, pp. 815-845.

[17]. Byrd W. E., Holk E., Friedman D. P. miniKanren, Live and Untagged: Quine Generation via Relational Interpreters (Programming Pearl), Workshop on Scheme and Functional Programming, 2012.

[18]. Kosarev D., Boulytchev D. Typed Embedding of a Relational Language in OCaml. ACM SIGPLAN Workshop on ML, 2016.

[19]. Язык OCanren. URL: http://github.com/dboulytchev/ocanren (accessed 09.04.2018).

[20]. Alvis C. E., Willcock J. J., Byrd W. E. cKanren: miniKanren with Constraints, Workshop on Scheme and Functional Programming, 2011.

[21]. Byrd W. E., Ballantyne M., Rosenblatt G., Might M. A Unified Approach to Solving Seven Programming Problems (Functional Pearl). Proc. ACM Program. Lang, 2017, vol. 1, ICFP, pp. 8:1-8:26.

[22]. Barendregt H. Lambda Calculi with Types. Handbook of Logic in Computer Science, Volume II, Oxford University Press, 1993.

i Надоели баннеры? Вы всегда можете отключить рекламу.