Яку частину Гіндлі-Мілнера ви не розумієте?


850

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


Яка частина

Хіндлі-Мілнер

ти не розумієш?


У моєму випадку відповідь буде ... все це!

Зокрема, я часто бачу подібні позначення в роботах Haskell, але я не маю поняття, що це означає. Я поняття не маю, якою галуззю математики це має бути.

Я розпізнаю букви грецького алфавіту звичайно та символи типу "" "(що зазвичай означає, що щось не є елементом набору).

З іншого боку, я ніколи раніше не бачив "⊢" ( Вікіпедія стверджує, що це може означати "розділ" ). Мені також незнайоме використання тут вінкулуму. (Зазвичай він позначає дріб, але це, мабуть, не так.)

Якби хтось міг хоча б сказати мені, з чого почати шукати, щоб зрозуміти, що означає це море символів, це було б корисно.


8
Якщо будуть шукає гарне пояснення алгоритму, найкраще , що я знайшов до сих пір знаходиться в главі 30 Шрайр Крішнамерті в Мови програмування: Застосування та інтерпретація (! CC ліцензійний).
laslowh

2
@laslowh Дякую! Я це читаю. Більш нова
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має тип σ. Ви можете прочитати його так само, як і Haskell 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 або lambda всередині якогось виразу; цей вираз зараз залежить від значення вільної змінної від її контексту. Правило говорить, що якщо є якась змінна, αяка не є "вільною" ні в чому у вашому контексті, то можна з упевненістю сказати, що будь-яке вираження, тип якого ви знаєте e : σбуде мати цей тип для будь-якого значення α.

Як користуватися правилами

Отже, тепер, коли ви розумієте символи, що ви робите з цими правилами? Ну, ви можете використовувати ці правила, щоб визначити тип різних значень. Для цього подивіться на свій вираз (скажіть f x y) і знайдіть правило, яке має висновок (нижня частина), що відповідає вашому твердженню. Давайте назвемо те, що ви намагаєтеся знайти свою "мету". У цьому випадку ви подивитесь на правило, яке закінчується e₀ e₁. Виявивши це, тепер вам доведеться знайти правила, що підтверджують все, що знаходиться вище рядка цього правила. Ці речі, як правило, відповідають типам суб-виразів, тому ви, по суті, повторюєтесь на частини виразу. Ви просто робите це, поки не закінчите своє дерево підтвердження, яке дає вам підтвердження типу вашого вираження.

Отже, усі ці правила - це точно вказати - і у звичайній математично педантичній деталі: P - як визначити типи виразів.

Тепер це має здатися знайомим, якщо ви коли-небудь використовували Prolog - ви по суті обчислюєте дерево доказів, як людський перекладач Prolog. Є причина, яку Prolog називають "логічним програмуванням"! Це також важливо, оскільки першим способом мене познайомили з алгоритмом висновку HM шляхом його впровадження в Prolog. Це насправді напрочуд просто і робить те, що відбувається далі. Вам неодмінно слід спробувати.

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


5
\ alpha - це змінна величина, яка не є вільною, а не звичайна змінна. Тому для пояснення правила узагальнення потрібно пояснити набагато більше.
nponeccop

2
@nponeccop: Хм, хороший момент. Я раніше ще не бачив цього конкретного правила. Не могли б ви допомогти мені пояснити це правильно?
Тихон Єлвіс

8
@TikhonJelvis: Це насправді досить просто, це дозволяє узагальнити (припускаючи Γ = {x : τ}) λy.x : σ → τдо ∀ σ. σ → τ, але не ∀ τ. σ → τ, тому що τце вільна змінна в Γ. Стаття Вікіпедії про HM пояснює це досить добре.
Вітус

7
Я вважаю, що частина відповіді, що стосується [Inst], трохи неточна. Це тільки моє розуміння поки що, але сигми в правилах [Inst]і [Gen]правила стосуються не типів, а тип-схем . Таким чином, оператор - це часткове замовлення, не пов'язане з підтипом, оскільки ми це знаємо з мов OO. Це пов'язано з поліморфними значеннями, такими як 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
  • τ type - τ - тип
  • e : τ - вираз e має тип τ
  • ev - вираз e має значення v

У суді зазначено, що один або кілька синтаксичних об'єктів мають властивість або стоять у деякому відношенні один до одного. Власність чи відношення самі по собі називаються формою судження , а судження про те, що предмет чи предмети мають це властивість чи виступає в цьому відношенні, є примірником цієї форми судження. Форма судження також називається предикатом , а об'єкти, що складають екземпляр, є її суб'єктами . Запишемо в J для вирішення стверджує , що J трюмах . Коли не важливо наголошувати на предметі судження, (текст тут відрізається)


53

Як я розумію правила Гіндлі-Мілнера?

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

Символи та позначення

Спочатку пояснимо символи та обговоримо пріоритет оператора

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

  • нехай 𝑥 = 𝑒₀ в 𝑒₁ означає у виразі 𝑒₁ , підміняючи 𝑒₀ там, де 𝑥 .

  • означає, що попередній елемент є підтипом (неофіційно - підкласом) останнього елемента.

  • 𝛼 - змінна тип.
  • 𝛼.𝜎 - це тип, ∀ (для всіх) змінних аргументів, 𝛼 , що повертає 𝜎 вираз
  • free (𝚪) означає не елемент змінних вільного типу 𝚪, визначених у зовнішньому контексті. (Зв'язані змінні є замінними.)

Все над лінією є передумовою, все нижче - висновком ( Пер Мартін-Леф )

Прецедент, на прикладі

Я взяв деякі більш складні приклади з правил і вставив зайві дужки, які мають перевагу:

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

  • 𝚪 ⊦ нехай 𝑥 = 𝑒₀ в 𝑒₁ : 𝜏 рівнозначно 𝚪 ⊦ (( нехай ( 𝑥 = 𝑒₀ ) в 𝑒₁ ): 𝜏 )

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

Тоді великі пробіли, що розділяють твердження про твердження та інші передумови, вказують на набір таких передумов, і нарешті горизонтальна лінія, що відокремлює передумови від висновку, підводить до кінця порядку пріоритетності.

Правила

Далі випливають тлумачення правил англійською мовою, за якими слідує неоднозначне перерахування та пояснення.

Змінна

VAR логічна схема

Даний 𝑥 - це тип 𝑥 (сигма), елемент Gam (гамма),
зробимо висновок ser стверджує a є 𝑥.

Інакше кажучи, в 𝚪 ми знаємо, що 𝑥 типу 𝜎, оскільки 𝑥 типу 𝑥 in 𝚪.

Це в основному тавтологія. Ім’я ідентифікатора - це змінна або функція.

Функція Застосування

APP логічна схема

Враховуючи 𝚪 твердження 𝑒₀ - це функціональний тип, а 𝚪 твердження 𝑒₁ - це 𝜏
висновок 𝚪 твердження, що застосовують функцію 𝑒₀ до 𝑒₁, це тип 𝜏

Щоб перезапустити правило, ми знаємо, що додаток функції повертає тип 𝜏 ', оскільки функція має тип 𝜏 → 𝜏' і отримує аргумент типу 𝜏.

Це означає, що якщо ми знаємо, що функція повертає тип, і ми застосовуємо його до аргументу, результатом буде екземпляр типу, який ми знаємо, що він повертає.

Функція абстракції

ABS-логічна схема

З огляду на 𝚪 і 𝑥 типу 𝜏 стверджує 𝑒 - тип,
conclude ' висновок »припускає анонімну функцію, 𝜆 повернення виразу, expression типу 𝑒 → 𝜏'.

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

Якщо ми знаємо, що 𝑥 типу 𝜏 і, таким чином, вираз 𝑒 має тип 𝑒 ', то функція 𝑥 повернення виразу 𝑒 має тип 𝑒 → 𝜏'.

Нехай оголошення змінної

LET логічна схема

Беручи під увагу Γ стверджує 𝑒₀, типу а, і Γ і 𝑥, типу а, стверджує 𝑒₁ типу Т
укладення Г стверджує let𝑥 = 𝑒₀ in𝑒₁ типу т

Вільно, 𝑥 пов'язаний з 𝑒₀ в 𝑒₁ (a 𝜏), тому що 𝑒₀ є 𝜎, а 𝑥 є 𝜎, що стверджує 𝑒₁ є 𝜏.

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

Моментальність

Логічна схема INST

З огляду на 𝚪 твердження 𝑒 типу 𝜎 'і 𝜎' є підтипом 𝜎
висновку 𝚪 твердження 𝑒 типу 𝜎

Вираз 𝑒 має батьківський тип 𝜎, оскільки вираз 𝑒 є підтипом 𝜎 ', а 𝜎 є батьківським типом 𝜎'.

Якщо екземпляр має тип, який є підтипом іншого типу, то це також екземпляр цього супертипу - більш загального типу.

Узагальнення

Логічна схема GEN

З огляду на 𝚪 твердження 𝑒 є 𝜎 і 𝜎 не є елементом вільних змінних 𝚪,
зробіть висновок 𝚪 твердження 𝑒, введіть для всіх виразів аргументів 𝛼, повертаючи a вираз

Отже, in вводиться 𝑒 для всіх змінних аргументів (𝛼), що повертаються 𝜎, оскільки ми знаємо, що 𝑒 є 𝜎 і 𝛼 не є вільною змінною.

Це означає, що ми можемо узагальнити програму, щоб прийняти всі типи для аргументів, які вже не пов'язані в області, що містить (змінні, які не є локальними). Ці зв'язані змінні є замінними.

Збираючи все це разом

Враховуючи певні припущення (наприклад, відсутність вільних / невизначених змінних, відоме середовище), ми знаємо типи:

  • атомні елементи наших програм (Змінна),
  • значення, повернені функціями (функціональна програма),
  • функціональні конструкції (функція абстракції),
  • нехай прив'язки (нехай мінливі декларації),
  • батьківські типи екземплярів (Instantiation) та
  • усі вирази (Узагальнення).

Висновок

Ці правила у поєднанні дозволяють довести найзагальніший тип затвердженої програми, не вимагаючи анотацій типів.


1
такий хороший резюме Аарон!
Бхурлоу

48

Позначення походить від природної дедукції .

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

6 правил дуже прості.

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

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

Absі Letє правилами для виведення типів лямбда-абстракції та введення.

Inst правило говорить, що ви можете замінити тип менш загальним.


4
Це послідовне обчислення, а не природна дедукція.
Роман Чепляка

12
@RomanCheplyaka добре, позначення майже однакові. У статті вікіпедії є цікаве порівняння двох методик: en.wikipedia.org/wiki/Natural_deduction#Sequent_calculus . Послідовне обчислення народжувалося в прямій відповіді на невдачі природного дедукції, тому якщо питання "звідки взялася ця нотація", то "природна дедукція" технічно є правильнішою відповіддю.
Ден Бертон

2
@RomanCheplyaka Інша думка полягає в тому, що послідовне обчислення є чисто синтаксичним (саме тому існує стільки структурних правил), поки це позначення не є. Перше правило передбачає, що контекст є набором, тоді як в послідовній обчисленні це простіший синтаксичний конструкт.
nponeccop

@Cheplyaka насправді, ні, він має щось схоже на "послідовність", але це не послідовне обчислення. Хапер розвиває розуміння цього в своїй текстовій книзі як «судження вищого порядку». Це дійсно природна дедукція.
Philip JF

15

Є два способи думати про e: σ. Одне - "вираз e має тип σ", інше - "впорядкована пара виразу e і тип σ".

Розгляньте Γ як знання про типи виразів, реалізовані як сукупність пар виразів і типів, e: σ.

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

Перше правило [Var] може бути прочитане:
Якщо наші знання Γ містять пару e: σ, то ми можемо вивести з from, що e має тип σ.

Друге правило [додаток] можна прочитати:
Якщо ми з Γ можемо вивести, що e_0 має тип τ → τ ', а ми з Γ можемо вивести, що e_1 має тип τ, то ми з Γ можемо вивести, що e_0 e_1 має тип τ '.

Зазвичай пишемо Γ, e: σ замість Γ ∪ {e: σ}.

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

Четверте правило [Нехай] залишається як вправа. :-)

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

Шосте і останнє правило [Gen] можна прочитати:
Якщо ми з Γ можемо вивести, що e має тип σ, а α не є змінною вільного типу в жодному з типів у Γ, то ми з Γ можемо зробити висновок, що e має тип ∀α σ.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.