Какую часть Хиндли-Милнера вы не понимаете?

851

Я клянусь , там раньше была футболкой для продажи с участием бессмертных слов:


Какая часть

Хиндли-Милнера

ты не понимаешь?


В моем случае ответ будет ... все это!

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

Я, конечно, узнаю буквы греческого алфавита и такие символы, как «∉» (что обычно означает, что что-то не является элементом набора).

С другой стороны, я никогда раньше не видел «⊢» ( Википедия утверждает, что это может означать «раздел» ). Я также незнаком с использованием винкулума здесь. ( Как правило, это означает фракцию, но не появляется , чтобы быть в этом случае.)

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

MathematicalOrchid
источник
8
Если будут ищет хорошее объяснение алгоритма, самое лучшее , что я нашел до сих пор находится в главе 30 Шрайр Кришнамерти в Языки программирования: Применение и интерпретация (! CC лицензионный).
Ласлоу
2
@laslowh Спасибо! Я читаю это. Более новая версия: cs.brown.edu/courses/cs173/2012/book/book.pdf
SnowOnion

Ответы:

652
  • В горизонтальной панели означает , что «[выше] подразумевает [ниже]».
  • Если в [выше] есть несколько выражений , рассмотрите их как объединенные; все [выше] должно быть верно, чтобы гарантировать [ниже].
  • :значит имеет тип
  • значит в . (Точно так же означает «не в».)
  • Γобычно используется для обозначения среды или контекста; в этом случае его можно рассматривать как набор аннотаций типов, связывающих идентификатор с его типом. Следовательно, x : σ ∈ Γозначает, что среда Γвключает в себя тот факт, что xимеет тип σ.
  • может быть прочитано как доказывает или определяет. Γ ⊢ x : σозначает, что среда Γопределяет, что xимеет тип σ.
  • ,это способ включения конкретных дополнительных допущений в окружающую среду Γ.
    Таким образом, Γ, x : τ ⊢ e : τ'означает , что окружающая среда Γ, с дополнительным, отвергая предположение , что xимеет типτ , доказывает , что eимеет тип τ'.

По запросу: приоритет оператора, от наивысшего к низшему:

  • Язык конкретного инфиксные и mixfix операторы, такие как λ x . e, ∀ α . σ, и τ → τ', let x = e0 in e1и пробельная для применения функции.
  • :
  • а также
  • , (Левоассоциативные)
  • пробел, разделяющий несколько предложений (ассоциативный)
  • турник
Дэн Бертон
источник
19
Каковы правила приоритета операторов?
Randomblue
:и очень похожи в том смысле, что они означают, что одна вещь содержится в другой вещи: набор содержит элементы, а тип в некотором смысле содержит значения. Принципиальное отличие состоит в том, x ∈ Sчто это означает, что набор Sбуквально содержит элемент x, тогда как Γ ⊢ x : Tозначает, что он xможет быть выведен для обитания типа Tв контексте Γ. Учитывая это, правило Var гласит: «Если x буквально содержится в контексте, из него можно (тривиально) вывести«.
Дэвид
@Randomblue я сделал явный приоритет символов, добавляя скобки везде, например (Γ,(x:τ))⊢(x:σ), см overleaf.com/read/ddmnkzjtnqbd#/61990222
SnowOnion
327

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

Символы

Следует также помнить, что некоторые буквы имеют традиционное значение; в частности, Γ представляет «контекст», в котором вы находитесь, то есть типы других вещей, которые вы видели. Итак, что-то вроде Γ ⊢ ...означает «выражение, ...когда вы знаете типы каждого выражения в Γ.

Символ по существу означает , что вы можете что - то доказать. Так Γ ⊢ ...же как и утверждение «Я могу доказать ...в контексте Γ. Эти утверждения также называются суждениями типа».

Следует иметь в виду еще одну вещь: в математике, как и в ML и Scala, x : σподразумевается, что у xнее есть тип σ. Вы можете прочитать это так же, как у Хаскелла x :: σ.

Что означает каждое правило

Итак, зная это, первое выражение становится легко понять: если мы знаем, что x : σ ∈ Γ(то есть, xимеет некоторый тип σв некотором контексте Γ), то мы знаем, что Γ ⊢ x : σ(то есть Γ, xимеет тип σ). Так что на самом деле, это не говорит вам ничего супер-интересного; это просто говорит вам, как использовать ваш контекст.

Другие правила также просты. Например, взять [App]. Это правило имеет два условия: e₀это функция от некоторого типа τк некоторому типу τ'и e₁значение типа τ. Теперь вы знаете, какой тип вы получите, обратившись e₀к e₁! Надеюсь, это не удивительно :).

Следующее правило имеет еще один новый синтаксис. В частности, Γ, x : τпросто означает контекст Γи суждение x : τ. Итак, если мы знаем, что переменная xимеет тип, τа выражение eимеет тип τ', мы также знаем тип функции, которая принимает xи возвращает e. Это просто говорит нам, что делать, если мы выяснили, какой тип принимает функция и какой тип она возвращает, поэтому это тоже не должно удивлять.

Следующий просто говорит вам, как обрабатывать letзаявления. Если вы знаете, что какое-то выражение e₁имеет тип τдо тех пор, пока оно xимеет тип σ, то letвыражение, которое локально связывается xсо значением типа, σбудет e₁иметь тип τ. На самом деле, это просто говорит вам, что оператор let по сути позволяет вам расширять контекст с помощью новой привязки, что и letделает!

[Inst]Правило касается суб-печати. Это говорит о том, что если у вас есть значение типа σ'и оно является подтипом σ( представляет отношение частичного упорядочения), то это выражение также имеет тип σ.

Последнее правило касается обобщающих типов. Небольшое отступление: свободная переменная - это переменная, которая не вводится оператором let или лямбда-выражением внутри некоторого выражения; это выражение теперь зависит от значения свободной переменной из ее контекста. Правило гласит, что если есть некоторая переменная, αкоторая не является «свободной» ни в чем в вашем контексте, то можно с уверенностью сказать, что любое выражение, тип которого вы знаете, e : σбудет иметь этот тип для любого значения α.

Как пользоваться правилами

Итак, теперь, когда вы понимаете символы, что вы делаете с этими правилами? Ну, вы можете использовать эти правила, чтобы выяснить тип различных значений. Чтобы сделать это, посмотрите на свое выражение (скажем f x y) и найдите правило, в котором есть заключение (нижняя часть), соответствующее вашему утверждению. Давайте назовем то, что вы пытаетесь найти своей «целью». В этом случае вы бы посмотрели на правило, которое заканчивается e₀ e₁. Когда вы нашли это, теперь вы должны найти правила, доказывающие все, что находится выше линии этого правила. Эти вещи, как правило, соответствуют типам подвыражений, так что вы по существу повторяете части выражения. Вы просто делаете это, пока не закончите свое дерево доказательств, которое дает вам подтверждение типа вашего выражения.

Таким образом, все эти правила указывают точно - и в обычной математически педантичной детали: P - как выяснить типы выражений.

Теперь, это должно звучать знакомо, если вы когда-либо использовали Пролог - вы по сути вычисляете дерево доказательств, как человеческий интерпретатор Пролога. Есть причина, по которой Пролог называют «логическим программированием»! Это также важно, так как я впервые познакомился с алгоритмом вывода HM, реализовав его в Прологе. Это на самом деле удивительно просто и проясняет то, что происходит. Вы должны обязательно попробовать это.

Примечание: я, вероятно, допустил некоторые ошибки в этом объяснении и был бы рад, если бы кто-то на них указал. На самом деле я расскажу об этом в классе через пару недель, поэтому я буду более уверенным в этом: P.

Тихон Джелвис
источник
5
\ alpha - это переменная несвободного типа, а не обычная переменная. Таким образом, чтобы объяснить правило обобщения, нужно объяснить гораздо больше.
nponeccop
2
@nponeccop: Хмм, хорошая мысль. Я на самом деле не видел это конкретное правило раньше. Не могли бы вы помочь мне объяснить это правильно?
Тихон Джелвис
8
@TikhonJelvis: Это на самом деле довольно проста, она позволяет обобщать (при условии Γ = {x : τ}) λy.x : σ → τдо ∀ σ. σ → τ, но не ∀ τ. σ → τ, потому что τэто свободная переменная Γ. Статья в Википедии об HM объясняет это довольно хорошо.
Витус
7
Я считаю, что часть ответа, связанная с этим, [Inst]немного неточна. Это только мое понимание до сих пор, но сигмы в [Inst]и [Gen]правила не относятся к типам, а тип-схемы . Таким образом, оператор является частичным порядком, не связанным с подтипом, как мы его знаем из ОО-языков. Это связано с полиморфными значениями, такими как id = λx. x. Полный синтаксис для такой функции был бы id = ∀x. λx. x. Теперь мы можем, очевидно, иметь id2 = ∀xy. λx. x, где yне используется. Тогда id2 ⊑ idэто то, что [Inst]говорит правило.
Ionuț G. Stan
71

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

См. « Практические основы языков программирования », главы 2 и 3, о стиле логики через суждения и выводы. Вся книга теперь доступна на Amazon.

Глава 2

Индуктивные определения

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

2.1 Суждения

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

  • n nat - n натуральное число
  • n = n1 + n2 - n это сумма n1 и n2
  • τ типа - τ типа
  • e : τ - выражение e имеет тип τ
  • ev - выражение e имеет значение v

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

Дон стюарт
источник
53

Как я понимаю правила Хиндли-Милнера?

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

Символы и обозначения

Сначала объясним символы и обсудим приоритет операторов

  • 𝑥 - это идентификатор (неофициально, имя переменной).
  • : средство - это тип (неофициально, экземпляр или "is-a").
  • 𝜎 (сигма) - это выражение, которое является либо переменной, либо функцией.
  • таким образом 𝑥: read читается как « 𝑥 is-a 𝜎 »
  • ∈ означает «является элементом»
  • 𝚪 (Гамма) это среда.
  • (знак утверждения) означает, что утверждает (или доказывает, но контекстуально «утверждает» читается лучше.)
  • ⊦ Г 𝑥 : σ Таким образом , следующим образом: «Г утверждает , что 𝑥, является разновидностью σ »
  • an является фактическим экземпляром (элементом) типа 𝜎 .
  • 𝜏 (тау) - это тип: базовый, переменный ( 𝛼 ), функциональный 𝜏 → 𝜏 ' или продукт 𝜏 × 𝜏' (продукт здесь не используется)
  • 𝜏 → 𝜏 ' - это функциональный тип, где 𝜏 и 𝜏' потенциально разные типы.
  • 𝜆𝑥.𝑒 означает, что 𝜆 (лямбда) является анонимной функцией, которая принимает аргумент 𝑥 и возвращает выражение 𝑒 .

  • пусть 𝑥 = 𝑒₀ в 𝑒₁ означает в выражении 𝑒₁ , замените 𝑒₀ везде, где 𝑥 появляется.

  • означает, что предыдущий элемент является подтипом (неофициально - подкласс) последнего элемента.

  • α является тип переменной.
  • 𝛼.𝜎 - это тип, ∀ (для всех) переменных аргумента, 𝛼 , возвращающее выражение 𝜎
  • free (𝚪) означает не элемент переменных свободного типа из type, определенных во внешнем контексте. (Связанные переменные являются заменяемыми.)

Все выше линии - предпосылка, все ниже - заключение ( Пер Мартин-Лёф )

Приоритет, на примере

Я взял некоторые из более сложных примеров из правил и вставил лишние скобки, которые показывают приоритет:

  • 𝑥: 𝜎 ∈ 𝚪 можно записать (𝑥: 𝜎) ∈ 𝚪
  • 𝚪 ⊦ 𝑥 : 𝜎 можно написать 𝚪 ⊦ ( 𝑥 : 𝜎 )

  • Г ⊦ пусть 𝑥 = 𝑒₀ в 𝑒₁ : τ это равносильно Γ ⊦ (( пусть ( 𝑥 = 𝑒₀ ) в 𝑒₁ ): τ )

  • 𝚪 ⊦ 𝜆𝑥.𝑒 : 𝜏 → 𝜏 ' эквивалентно 𝚪 ⊦ (( 𝜆𝑥.𝑒 ): ( 𝜏 → 𝜏' ))

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

Правила

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

переменная

VAR Logic Diagram

Поскольку 𝑥 является типом 𝜎 (сигма), элементом an (гамма),
заключаем, что 𝚪 утверждает, что 𝑥 является 𝜎.

Другими словами, в 𝚪 мы знаем, что 𝑥 имеет тип 𝜎, потому что 𝑥 имеет тип 𝜎 в 𝚪.

Это в основном тавтология. Имя идентификатора - это переменная или функция.

Применение функции

Логическая схема приложения

Учитывая, что 𝚪 asserts 𝑒₀ является функциональным типом, а 𝚪 asserts 𝑒₁ является 𝜏
заключением ser утверждает применение функции 𝑒₀ к 𝑒₁ типу 𝜏

Чтобы переформулировать правило, мы знаем, что приложение функции возвращает тип 𝜏 ', потому что функция имеет тип 𝜏 → 𝜏' и получает аргумент типа 𝜏.

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

Функция Абстракция

ABS логическая схема

Учитывая, что 𝚪 и 𝑥 типа 𝜏 утверждает 𝑒, является типом, 𝜏 «
заключить» утверждает анонимную функцию, 𝜆 из 𝑥 возвращающего выражение, 𝑒 имеет тип 𝜏 → 𝜏 '.

Опять же, когда мы видим функцию, которая принимает 𝑥 и возвращает выражение 𝑒, мы знаем, что она имеет тип 𝜏 → 𝜏 ', потому что 𝑥 (a 𝜏) утверждает, что 𝑒 является 𝜏'.

Если мы знаем, что 𝑥 имеет тип 𝜏 и, следовательно, выражение 𝑒 имеет тип 𝜏 ', то функция of, возвращающая выражение 𝑒, имеет тип 𝜏 → 𝜏'.

Пусть объявление переменной

LET Logic Diagram

Учитывая, что 𝚪 утверждает 𝑒₀ типа 𝜎 и 𝚪 и 𝑥 типа 𝜎, утверждает 𝑒₁ типа 𝜏
заключить 𝚪 утверждает let𝑥 = 𝑒₀ in𝑒₁ типа 𝜏

Слабо, 𝑥 связан с 𝑒₀ в 𝑒₁ (a 𝜏), потому что 𝑒₀ является 𝜎, а 𝑥 является 𝜎, который утверждает, что 𝑒₁ является 𝜏.

Это означает, что если у нас есть выражение 𝑒₀, представляющее собой 𝜎 (являющееся переменной или функцией), и некоторое имя 𝑥, также a a, и выражение 𝑒₁ типа 𝜏, то мы можем заменить 𝑒₀ на 𝑒₀, где бы оно ни находилось внутри 𝑒₁.

Конкретизация

INST Логическая Диаграмма

Дано 𝚪 утверждает 𝑒 типа 𝜎 ', а 𝜎' является подтипом 𝜎
заключить 𝚪 утверждает 𝑒 типа 𝜎

Выражение 𝑒 имеет родительский тип 𝜎, поскольку выражение 𝑒 является подтипом 𝜎 ', а 𝜎 является родительским типом 𝜎'.

Если экземпляр имеет тип, который является подтипом другого типа, то он также является экземпляром этого супертипа - более общего типа.

Обобщение

GEN логическая схема

Если 𝚪 asserts 𝑒 является 𝜎 и 𝛼 не является элементом свободных переменных 𝚪,
заключите 𝚪 asserts 𝑒, введите для всех аргументов выражения 𝛼, возвращающих выражение 𝜎

Таким образом, в общем случае, 𝑒 типизирован 𝜎 для всех переменных аргумента (𝛼), возвращающих 𝜎, потому что мы знаем, что 𝑒 является 𝜎, а 𝛼 не является свободной переменной.

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

Собираем все вместе

Учитывая определенные предположения (такие как отсутствие свободных / неопределенных переменных, известная среда), мы знаем типы:

  • атомарные элементы наших программ (переменные),
  • значения, возвращаемые функциями (Function Application),
  • функциональные конструкции (Function Abstraction),
  • пусть привязки (пусть объявления переменных),
  • родительские типы экземпляров (Instantiation) и
  • все выражения (обобщение).

Вывод

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

Аарон Холл
источник
1
такой хороший итог Аарон!
bhurlow
48

Обозначение происходит от естественного вывода .

⊢ символ называется турникет .

6 правил очень просты.

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

AppПравило говорит, что если у вас есть два идентификатора e0и вы e1можете определить их типы, вы можете определить тип приложения e0 e1. Правило читается так, если вы знаете это e0 :: t0 -> t1и e1 :: t0(то же самое t0!), Тогда приложение хорошо напечатано, а тип - t1.

Absи Letявляются правилами для вывода типов для лямбда-абстракции и ввода.

Inst Правило говорит, что вы можете заменить тип менее общим.

nponeccop
источник
4
Это последовательное исчисление, а не естественный вывод.
Роман Чепляка
12
@RomanCheplyaka хорошо, запись почти такая же. В статье в Википедии есть интересное сравнение двух техник: en.wikipedia.org/wiki/Natural_deduction#Sequent_calculus . Секвенциальное исчисление было рождено как прямой ответ на недостатки естественной дедукции, поэтому, если вопрос «откуда взялась эта запись», то «естественная дедукция» технически является более правильным ответом.
Дэн Бертон,
2
@RomanCheplyaka Другое соображение заключается в том, что секвенциальное исчисление является чисто синтаксическим (вот почему существует так много структурных правил), в то время как это обозначение - нет. Первое правило предполагает, что контекст является множеством, в то время как в последовательном исчислении это более простая синтаксическая конструкция.
nponeccop
@ Чепляка, на самом деле, нет, у него есть нечто, похожее на "секвенция", но это не секвенционное исчисление. Хапер развивает понимание этого в своей учебной книге как «суждение более высокого порядка». Это действительно естественный вывод.
Филипп JF
15

Есть два способа думать о е: σ. Одним из них является «выражение e имеет тип σ», другим является «упорядоченная пара выражения e и тип σ».

Рассматривайте Γ как знания о типах выражений, реализованные как набор пар выражений и типов, e: σ.

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

Таким образом, первое правило [Var] можно прочитать:
если наше знание Γ содержит пару e: σ, то мы можем вывести из Γ, что e имеет тип σ.

Второе правило [App] можно прочитать:
если мы из Γ можем вывести, что e_0 имеет тип τ → τ ', и мы из Γ можем вывести, что e_1 имеет тип τ, то мы из Γ можем вывести, что e_0 e_1 имеет Тип τ '.

Обычно пишут Γ, e: σ вместо Γ ∪ {e: σ}.

Таким образом, можно прочитать третье правило [Abs]:
если мы из Γ, расширенного с помощью x: τ, можем сделать вывод, что e имеет тип τ ', то мы из Γ можем сделать вывод, что λx.e имеет тип τ → τ'.

Четвертое правило [Позвольте] оставлено в качестве упражнения. :-)

Пятое правило [Inst] можно прочитать:
если мы из Γ можем вывести, что e имеет тип σ ', а σ' является подтипом σ, то мы из Γ можем вывести, что e имеет тип σ.

Шестое и последнее правило [Gen] можно прочитать:
если мы из Γ можем вывести, что e имеет тип σ, и α не является переменной свободного типа ни в одном из типов в Γ, то мы из Γ можем вывести, что e имеет тип ∀α σ.

Пер Перссон
источник