Вывод типа выражения в ML - PullRequest
5 голосов
/ 16 июля 2010

Все,

Я хочу получить выражение типа для функции ниже в ML:

fun f x y z = y (x z)

Теперь я знаю, что при наборе того же самого выражения будет генерироваться выражение типа.Но я хочу вывести эти значения вручную.

Также, пожалуйста, укажите общие шаги, которые необходимо соблюдать при получении выражений типа.

Ответы [ 3 ]

8 голосов
/ 18 июля 2010

Я попытаюсь сделать это максимально механическим способом, точно так же, как это было бы реализовано в большинстве компиляторов.

Давайте разберем это:

fun f x y z = y (x z)

Этов основном сахар для:

val f = fn x => fn y => fn z => y (x z)

Давайте добавим некоторые переменные метасинтаксического типа (это не настоящие SML-типы, просто заполнители для примера):

val f : TX = fn (x : T2) => fn (y : T3) => fn (z : T4) => y (x z) : T5

ОК,поэтому мы можем начать генерировать систему ограничений из этого.T5 - возможный тип возврата f.На данный момент, мы собираемся просто назвать возможный тип всей этой функции «TX» - некоторую свежую, неизвестную переменную типа.

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

Так что нам говорят приложения?

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

y (x z)

z не применяется ни к чему, поэтому мы просто посмотрим, какая переменная типа мы присвоили ей ранее (T4), и используем ее в качестве своего типа.

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

T2 = T4 -> T7

y применяется к результату (xz), который мы только что назвали T7.Еще раз, мы еще не знаем тип возвращаемого значения y, поэтому мы просто передадим ему свежую переменную:

T3 = T7 -> T8

Мы также знаем, что тип возвращаемого значения y - это тип возвращаемого значения длявсе тело функции, которую мы назвали «T5» ранее, поэтому мы добавим ограничение:

T5 = T8

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

TX = T2 -> T3 -> T4 -> T5

Теперь мы собрали все ограничения:

val f : TX = fn (x : T2) => fn (y : T3) => fn (z : T4) => y (x z) : T5
TX = T2 -> T3 -> T4 -> T5
T2 = T4 -> T7
T3 = T7 -> T8
T5 = T8

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

val f : TX = fn (x : T2) => fn (y : T3) => fn (z : T4) => y (x z) : T8
TX = T2 -> T3 -> T4 -> T8
T2 = T4 -> T7
T3 = T7 -> T8

val f : TX = fn (x : T2) => fn (y : T7 -> T8) => fn (z : T4) => y (x z) : T8
TX = T2 -> (T7 -> T8) -> T4 -> T8
T2 = T4 -> T7

val f : TX = fn (x : T4 -> T7) => fn (y : T7 -> T8) => fn (z : T4) => y (x z) : T8
TX = (T4 -> T7) -> (T7 -> T8) -> T4 -> T8

val f : (T4 -> T7) -> (T7 -> T8) -> T4 -> T8 = fn (x : T4 -> T7) => fn (y : T7 -> T8) => fn (z : T4) => y (x z) : T8

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

val f : (T4 -> T7) -> (T7 -> T8) -> T4 -> T8

Последний шаг - обобщить все переменные типа, которые остались в более знакомые полиморфные типы, которые мы знаем и любим.По сути, это всего лишь проход, заменив первую переменную несвязанного типа на 'a, вторую на' b и т. Д.

val f : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

Я уверен, что вы найдете именно тот тип, который вашКомпилятор SML предложит и для этого термина.Я сделал это вручную и по памяти, поэтому извиняюсь, если что-то испортил: p

Мне было трудно найти хорошее объяснение этого процесса вывода и ограничения типа.Я использовал две книги, чтобы изучить его - «Реализация современного компилятора в ML» Эндрю Аппеля и «Типы и языки программирования» Пирса.Ни один из них не был полностью освещен для меня, но между ними я понял это.

3 голосов
/ 16 июля 2010

Чтобы определить тип чего-либо, нужно взглянуть на каждое место, где оно используется. Например, если вы видите val h = hd l, вы знаете, что l является списком (потому что hd принимает список в качестве аргумента), и вы также знаете, что тип h является типом, который l является список. Допустим, тип h равен a, а тип l равен a list (где a - это заполнитель). Теперь, если вы видите val h2 = h*2, вы знаете, что h и h2 равны int s, поскольку 2 - это целое число, вы можете умножить int на другое целое, а результат умножения двух целых чисел - это целое число. , Поскольку ранее мы говорили, что тип h равен a, это означает, что a равен int, поэтому тип l равен int list.

Итак, давайте рассмотрим вашу функцию:

Давайте рассмотрим выражения в том порядке, в котором они вычисляются: сначала вы делаете x z, т.е. вы применяете x к z. Это означает, что x является функцией, поэтому она имеет тип a -> b. Поскольку в качестве аргумента функции задан z, он должен иметь тип a. Тип x z для этого b, потому что это тип результата x.

Теперь вызывается y с результатом x z. Это означает, что y также является функцией, а ее тип аргумента является типом результата x, который равен b. Так что y имеет тип b -> c. Снова тип выражения y (x z) для этого c, потому что это тип результата y.

Поскольку это все выражения в функции, мы не можем ограничивать типы далее, поэтому наиболее общие типы для x, y и z: 'a -> 'b, 'b -> 'c и 'a соответственно и тип целого выражения 'c.

Это означает, что общий тип f равен ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

Для объяснения того, как типы выводятся программно, читайте о Вывод типа Хиндли – Милнера .

0 голосов
/ 17 июля 2010

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

Затем каждая конструкция в программе имеет уравнение, связывающее эти переменные типа.которые имеют отношение к этой конструкции.

Например, если программа содержит fx и 'a1 - переменная типа для f, а' a2 - переменная типа для x, а 'a3 - переменная типа для "fx ",

, тогда приложение приводит к уравнению типа: 'a1 =' a2 -> 'a3

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

...