Я работаю над небольшим компилятором лямбда-исчисления, который имеет работающую систему логического вывода типа Хиндли-Милнера, а теперь также поддерживает рекурсивный метод давайте (не в связанном коде), который, как я понимаю, должно быть достаточно для завершения Тьюринга .
Проблема сейчас в том, что я понятия не имею, как создать списки поддержки или уже поддерживает их, и мне просто нужно найти способ их кодирования. Я хотел бы иметь возможность определять их без добавления новых правил в систему типов.
Самый простой способ, которым я могу представить список, x
- это что-то, что есть null
(или пустой список), или пара, которая содержит как x
и, так и список x
. Но для этого мне нужно уметь определять пары и или, которые, как я считаю, являются продуктом и типом суммы.
Кажется, я могу определить пары таким образом:
pair = λabf.fab
first = λp.p(λab.a)
second = λp.p(λab.b)
Так как pair
будет иметь тип a -> (b -> ((a -> (b -> x)) -> x))
, после передачи, скажем, a int
и a string
, он даст что-то с типом (int -> (string -> x)) -> x
, который будет представлением пары int
и string
. Что меня здесь беспокоит, так это то, что если это представляет пару, то почему это логически не эквивалентно и не подразумевает предложение int and string
? Тем не менее, это эквивалентно (((int and string) -> x) -> x)
, как если бы я мог иметь только типы продуктов в качестве параметров для функций. Этот ответпохоже, решают эту проблему, но я понятия не имею, что означают символы, которые он использует. Кроме того, если это на самом деле не кодирует тип продукта, могу ли я что-то сделать с типами продуктов, которые я не смог бы сделать с моим определением пар выше (учитывая, что я также могу определять n-кортежи таким же образом)? Если нет, не будет ли это противоречить тому факту, что вы не можете выразить (AFAIK) соединение, используя только подтекст?
Кроме того, как насчет типа суммы? Можно ли как-то кодировать его, используя только тип функции? Если так, будет ли этого достаточно для определения списков? Или еще, есть ли другой способ определения списков без необходимости расширять мою систему типов? И если нет, то какие изменения мне нужно было бы сделать, если я хочу сделать это как можно более простым?
Пожалуйста, имейте в виду, что я программист, но не ученый и не математик, и довольно плохо читаю математические записи.
Изменить: я не уверен, что техническое название того, что я реализовал до сих пор, но все, что у меня есть, это в основном код, который я связал выше, это алгоритм генерации ограничений, который использует правила для приложений, абстракций и взятых переменных из алгоритма Хинли-Милнера, а затем алгоритм объединения, который получает основной тип. Например, выражение \a.a
выдаст тип a -> a
, а выражение \a.(a a)
сгенерирует ошибку проверки возникновения. Помимо этого, существует не совсем let
правило, а функция, которая, по-видимому, имеет тот же эффект, который позволяет вам определять рекурсивные глобальные функции, такие как этот псевдокод:
GetTypeOfGlobalFunction(term, globalScope, nameOfFunction)
{
// Here 'globalScope' contains a list of name-value pair where every value is of class 'ClosedType',
// meaning their type will be cloned before unified in the unification algorithm so that they can be used polymorphically
tempType = new TypeVariable() // Assign a dummy type to `tempType`, say, type 'x'.
// The next line creates an scope with everything in 'globalScope' plus the 'nameOfFunction = tempType' name-value pair
tempScope = new Scope(globalScope, nameOfFunction, tempType)
type = TypeOfTerm(term, tempScope) // Calculate the type of the term
Unify(tempType, type)
return type
// After returning, the code outside will create a 'ClosedType' using the returned type and add it to the global scope.
}
Код в основном получает тип термина как обычно, но перед объединением он добавляет имя функции, определяемой с помощью фиктивного типа, в область действия типа, чтобы его можно было использовать изнутри самого себя рекурсивно.
Редактировать 2: Я только что понял, что мне также понадобятся рекурсивные типы, которых у меня нет, чтобы определить список так, как я хочу.
let func = \x -> (func x)
) вы получите то, что у меня есть.