Вывод типа с типами продукта


15

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

Термин - это литерал, композиция терминов, цитата из термина или примитив.

e::=x|ee|[e]|…

Все термины обозначают функции. Для двух функций и , , то есть, сопоставление обозначает обратную композицию. Литералы обозначают ниладические функции.e1e2e1e2=e2∘e1

Термины, отличные от состава, имеют основные правила типа:

x:ι[Lit]Γ⊢e:σΓ⊢[e]:∀α.α→σ×α[Quot],α not free in Γ

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

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

τ::=ι|α|ρ→ρρ::=()|τ×ρσ::=τ|∀α.σ

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

Чтобы улучшить читаемость и сократить скобки, я предполагаю, что в схемах типов. Я также буду использовать заглавную букву для переменной, обозначающей стек, а не одно значение.ab=b×(a)

Есть шесть примитивов. Первые пять довольно безобидны. dupпринимает верхнее значение и производит две его копии. swapизменяет порядок двух верхних значений. popотбрасывает верхнее значение. quoteпринимает значение и создает цитату (функцию), которая его возвращает. applyприменяет цитату к стеку.

dup::∀Ab.Ab→Abbswap::∀Abc.Abc→Acbpop::∀Ab.Ab→Aquote::∀Ab.Ab→A(∀C.C→Cb)apply::∀AB.A(A→B)→B

Последний комбинатор composeдолжен взять две кавычки и вернуть тип их конкатенации, то есть . В статически типизированном конкатенативном языке Cat этот тип очень прост.[e1][e2]compose=[e1e2]compose

compose::∀ABCD.A(B→C)(C→D)→A(B→D)

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

compose::∀ABCDE.A(B→C)(D→E)→A…

Если вы позволите обозначать разницу двух типов, то я думаю, что вы можете написать тип правильно.∖compose

compose::∀ABCDE.A(B→C)(D→E)→A((D∖C)B→((C∖D)E))

Это по - прежнему относительно просто: composeпринимает функцию и один . Его результат потребляет поверх потребления не произведенного , и производит поверх производства не потребленного . Это дает правило для обычного состава.f1:B→Cf2:D→EBf2f1Df1f2

Γ⊢e1:∀AB.A→BΓ⊢e2:∀CD.C→DΓ⊢e1e2:((C∖B)A→((B∖C)D))[Comp]

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

∀A.()∖A=()∀A.A∖()=A∀ABCD.AB∖CD=B∖D iff A=Cotherwise=undefined

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


Как вы используете переменные в вашей грамматике? Этот вопрос должен помочь вам разобраться с «подтипом», который вам нужен.
— Джмад

1
@jmad: Я не уверен, что понимаю вопрос. Переменные типа существуют только для формального определения схем типов, а сам язык вообще не имеет переменных, только определения, которые могут быть [взаимно] рекурсивными.
— Джон Перди

Справедливо. Можете ли вы сказать, почему (возможно, с примером) правило для composeслишком ограничено? У меня сложилось впечатление, что это хорошо, как это. (например, ограничение может быть обработано объединением, как для применения, как в λ-исчислении)C=D
— jmad

@jmad: конечно. Рассмотрим twiceопределение как dup compose apply, которое берет цитату и применяет ее дважды. [1 +] twiceвсе в порядке: вы составляете две функции типа . Но это не так: если , проблема в том, что , поэтому выражение запрещено, хотя оно должно быть допустимым и иметь типа . Конечно, решение состоит в том, чтобы поместить классификатор в нужное место, но я в основном задаюсь вопросом, как на самом деле написать тип без некоторого циклического определения. ∀ A b .ι→ι[pop] twiceA ≠ A∀Ab.f1,f2:Ab→A∀ A b .A≠Ab∀Ab.Abb→Acompose
— Джон Пурди

Ответы:


9

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

compose:∀ABCδ.δ (∀α.α A→αB) (∀β.β B→βC)→δ (∀γ.γ A→γC)

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

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

Я считаю, что проверка типов типа ранга 2 в общем неразрешима, хотя была проделана некоторая работа, которая дает хорошие результаты на практике (для Haskell):

  • Саймон Л. Пейтон Джонс, Димитриос Витиниотис, Стефани Вейрих, Марк Шилдс: Практический вывод типов для типов произвольного ранга. J. Funct. Программа. 17 (1): 1-82 (2007)

Правило типа для композиции просто:

Γ⊢e1:∀α.α A→α BΓ⊢e1:∀α.α B→α CΓ⊢e1 e2:∀α.α A→α C

Чтобы система типов работала в целом, вам нужно следующее правило специализации:

Γ⊢e:∀α.α A→α BΓ⊢e:∀α.C A→α C B

Спасибо, это было очень полезно. Этот тип подходит для функций с одним аргументом, но он не поддерживает несколько аргументов. Например, dup +должен иметь тип потому что имеет тип . Но вывод типа при отсутствии аннотаций является абсолютным требованием, поэтому ясно, что мне нужно вернуться к чертежной доске. У меня есть идея для другого подхода, и я буду писать об этом, если это сработает. ι→ι+ιι→ι
— Джон Перди

1
Типы стека количественно определяют фрагменты стека, поэтому проблем с двумя аргументами не возникает. Я не уверен, как это относится dup +, так как это не использует compose, как вы определили это выше.
— Дэйв Кларк

Э, верно, я имел в виду [dup] [+] compose. Но я читаю как ; сказать ; тогда у вас есть а не . Вложение не является правильным, если вы не переверните стек так, чтобы верх был последним (самым глубоким вложенным) элементом. B × α B = ι × ι ( ι × ι ) × α ι × ( ι × α )αBB×αB=ι×ι(ι×ι)×αι×(ι×α)
— Джон Перди

Возможно, я строю свой стек в неправильном направлении. Я не думаю, что вложенность имеет значение, пока пары, составляющие стек, не появляются в языке программирования. (Я планирую обновить свой ответ, но сначала нужно провести небольшое исследование.)
— Дэйв Кларк

Да, вложение - это в значительной степени деталь реализации.
— Джон Перди
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.