«Доказом є програма; формула, яку він доводить, є типом для програми »


37

Це може бути питання філософського типу, але я вважаю, що на нього є об'єктивна відповідь.

Якщо ви читаєте статтю вікіпедії про Haskell, ви можете знайти таке:

Мова вкорінена в спостереженнях Haskell Curry та його інтелектуальних нащадків, що "доказ - це програма; формула, яку вона доводить, є типом для програми".

Тепер я запитую: чи це не стосується майже всіх мов програмування? Яка особливість (або набір функцій) Haskell робить його сумісним із цим твердженням? Іншими словами, якими помітними способами це твердження вплинуло на дизайн мови?


4
Комусь хочеться пояснити, чому голосують "закриті", будь ласка?

1
@ Григорій Джавадян: Я не голосував за закриття, але це, мабуть, тому, що питання є прикордонним поза темою для ТА - філософські питання, об'єктивно відповіді чи іншим чином, тут взагалі не підходять. У цьому випадку я думаю, що це виправдано, оскільки відповідь має глибокі практичні наслідки для того, як насправді використовується Haskell.

2
@Grigory: якщо це питання було справжньою проблемою з реальним рішенням (у коді), то воно може залишатися на SO. Голосували за закриття та перехід до програмістів.

9
Просто до цього, тому що я трохи розпалений - відповіді на це питання рясніють посиланнями на жорсткі дослідження CS і в цьому сенсі більш "об'єктивні", ніж 90% SO. Крім того, критерії шестилеточних змінних (що для рішення потрібен код) шалено вузькі для широкого кола справжніх питань програмування, які не є ні суб'єктивними, ні поза темою. Мені б дуже не хотілося бачити, щоб дискусія про включення / делеціоністство знову передавалася на SO, але якщо чітко програмувальні теми, як це,
натрапляють

2
Я неоднозначний щодо того, де це закінчується, здебільшого тому, що мені дійсно незрозуміло, який саме контент повинен бути у Programmers.SE vs. SO. Але я скажу, що програмісти описуються в декількох місцях як "суб'єктивні запитання", що це питання чітко не відповідає . Моя відповідь є настільки ж неофіційною і хвилеподібною, як це могло б бути, і я все-таки міг легко підкріпити більшу частину цього посилання з посиланнями, які приймають навіть вертикальні редактори Вікіпедії.
CA McCann

Відповіді:


38

Так, але дуже рідко корисна концепція є загальною концепцією.

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

Крім того, багато мов, які нібито мають "систему статичного типу", повинні розглядатися як динамічні на практиці в цьому контексті, оскільки вони дозволяють перевіряти та перетворювати типи під час виконання. Зокрема, це означає будь-яку мову із вбудованою підтримкою за замовчуванням для "рефлексії" тощо. Наприклад, C #.

Хаскелл незвичний тим, яку кількість інформації він очікує надати, зокрема, функції не можуть залежати від будь-якого значення, окрім вказаного в якості його аргументів. З іншого боку, мовою із змінними глобальними змінними будь-яка функція може (потенційно) перевіряти ці значення та відповідно змінювати поведінку. Таким чином , функція Haskell з типом A -> Bможна розглядати як програму прувінга мініатюрної , що Aпередбачає B; еквівалентна функція багатьох інших мов лише підказує нам, що Aі будь-яка глобальна держава є в поєднанні за обсягом B.

Зауважте, що хоча Haskell підтримує такі речі, як відображення та динамічні типи, використання таких функцій повинно бути зазначено у підписі типу функції; так само для використання глобальної держави. Жоден з них не доступний за замовчуванням.

У Haskell також існують способи розбиття речей, наприклад, допускаючи винятки під час виконання програми або використовуючи нестандартні примітивні операції, що надаються компілятором, але ті мають сильне сподівання, що вони будуть використовуватися лише з повним розумінням способів, які виграли ' t пошкодити значення зовнішнього коду. Теоретично те саме можна сказати і про інші мови, але на практиці з більшістю інших мов зробити речі важче, не «обманюючи», і менше нахмуритися на «обман». І, звичайно, у справжніх "динамічних" мовах все це залишається неактуальним.

Концепцію можна взяти набагато далі, ніж це в Haskell.


Зауважте, що винятки можуть бути повністю інтегровані в систему типів.
садок

18

Ви вірні, що листування Кері-Говарда - це дуже загальна річ. Варто ознайомитись трохи з його історією: http://en.wikipedia.org/wiki/Curry-Howard_cor korespondence

Ви зауважите, що, як було сформульовано спочатку, це листування особливо стосувалося інтуїтивістської логіки з одного боку та просто набраного лямбда-числення (STLC) з іншого.

Класичний Haskell - або 98 р., Або навіть більш ранні версії, він дуже близько до STLC, і здебільшого був дуже простий, прямий переклад між будь-яким заданим виразом в Haskell і відповідним терміном в STLC (розширений з рекурсією і кілька примітивних типів). Таким чином, це зробило Крірі-Говарда дуже явним. Сьогодні, завдяки розширенням, такий переклад - дещо складніша справа.

Тож у певному сенсі питання полягає в тому, чому Хаскелл так прямо «дегугарує» в STLC. Дві речі спадають на думку:

  • Типи. На відміну від Схеми, яка також є підданим числення лямбда-роду (серед іншого), Хаскелл сильно набраний. Це означає, що в класичному Haskell не існує термінів, які за визначенням не можуть бути добре набрані термінами в STLC.
  • Чистота. Знову ж таки, на відміну від Схеми, але як і STLC, Haskell - це чиста, референтно прозора мова. Це досить важливо. Мови з побічними ефектами можуть бути вбудовані в мови, які не мають побічних ефектів. Однак це - трансформація цілої програми, а не просто місцеве знешкодження. Отже, щоб мати пряме листування, необхідно, щоб ви почали з чисто функціональної мови.

Існує також важливий спосіб, коли Хаскелл, як і більшість мов, не вдається щодо прямого застосування листування Кері-Говарда. Haskell, як мова, що завершує тюрінг, містить можливість необмеженої рекурсії, а отже, і не припинення. STLC не має оператора фіксованої точки, не завершується і сильно нормалізується - це означає, що жодне скорочення терміна в STLC не закінчиться. Можливість рекурсії означає, що можна «обдурити» Карі-Говарда. Наприклад, let x = x in xмає типforall a. a- Тобто, оскільки він ніколи не повертається, я можу зробити вигляд, що він дає мені що завгодно! Оскільки ми завжди можемо це зробити в Haskell, це означає, що ми не можемо повністю «повірити» в будь-які докази, що відповідають програмі Haskell, якщо у нас немає окремого доказу того, що сама програма закінчується.

Рядок функціонального програмування до Хаскелла (зокрема, сім'ї ML) був результатом досліджень CS, орієнтованих на побудову мов, про які ви могли легко довести речі (серед іншого), дослідження, які дуже добре обізнані і виходячи з СН для початку. І навпаки, Haskell послужив як мовою хосту, так і натхненником для ряду допоміжних помічників, що розробляються, таких як Agda та Epigram, коріння яких розвиваються в теорії типів, дуже пов'язаних з походженням CH.


1
Було б добре підкреслити, що невинищення певним чином підриває доказ, який, хоча і очевидно катастрофічний з логічної точки зору, зберігає багато інших властивостей. Зокрема, функція A -> B, надана A, або буде створювати Bабо взагалі нічого не робити . Він ніколи не призведе до того C, яке значення типу Bвін надає або, якщо воно розходиться, все ще залежить виключно від Aнаданого.

@camccann - трохи нерозумний, але я б розрізнив дно та "зовсім нічого", що більше схоже Void, ні? Лінь робить все більш і менш складним і те, і інше. Я б сказав, що функція A -> B завжди створює значення типу B, але це значення може мати менше інформації, ніж можна було б очікувати.
sclv

Нитчікінг - це весело! Коли я кажу "нічого", я маю на увазі на рівні значення в контексті виконання оцінювання, тоді як дно дійсно існує лише як абстракція, а не щось відчутне. Вираз, що оцінюється, ніколи не буде "бачити" значення нижнього, просто терміни, які він не використовує (що може бути внизу), і терміни, які він використовує (які мають найдонні значення). Спроба використати нижнє "ніколи не буває" в якомусь сенсі, тому що спроба зробити це закінчує оцінку всього виразу до того, як використання станеться.

12

До наближення першого порядку, більшість інших (слабко та / або однотипні) мови не підтримують суворе розмежування рівня мови між

  • пропозиція (тобто тип)
  • доказ (тобто програма демонструє , як ми можемо побудувати речення з набору примітивів і / або інших вищих порядків конструкцій)

і суворе відношення між ними. Якщо що, найкращі гарантії надають інші такі мови

  • маючи обмежене обмеження на вхід, разом із тим, що трапляється в навколишньому середовищі в той час, ми можемо створити значення з обмеженим обмеженням. (традиційні статичні типи, cf C / Java)
  • кожна конструкція одного типу (динамічні типи, cf ruby ​​/ python)

Зауважте, що за типом ми посилаємось на пропозицію , а отже, щось, що описує набагато більше інформації, а не лише int або bool . У Haskell на культуру, яка пронизує функцію, впливають лише її аргументи - немає винятків *.

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

  • суворі пропозиції для всіх мовних примітивів
  • обмежений набір механізмів, за допомогою яких примітиви можуть поєднуватися

Конструкції Haskell, як правило, дуже добре піддаються міркуванням про свою поведінку. Якщо ми можемо побудувати доказ (читай: функція) , що доводять , що Aмає на увазі B, що це має дуже корисні властивості:

  • це завжди справедливо (доки у нас є A, ми можемо побудувати a B)
  • цей підтекст покладається тільки на A, і більше нічого .

тим самим дозволяючи нам ефективно міркувати про локальні / глобальні інваріанти. Повернутися до початкового питання; Мовні функції Haskell, які найкраще захоплюють цей спосіб мислення:

  • Чистота / Сегментація ефектів на явні конструкції (ефекти враховуються і набираються!)
  • Введіть висновки / перевірку в компіляторах Haskell
  • Можливість вбудовувати контрольні та / або інваріанти потоку даних у пропозиції / типи програми, що встановлюються програмою, щоб довести:
  • Референтна доброчесність

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

Це питання заслуговує досить глибокої відповіді, і я не міг би зробити це справедливим у цьому контексті. Я б запропонував прочитати більше на wikipedia / у літературі:

* Примітка: я переглядаю / ігнорую деякі складніші аспекти домішок Хаскелла (винятки, неприпинення тощо), які лише ускладнюють аргумент.


4

Яка особливість? Система типу (статична, чиста, поліморфна). Гарною відправною точкою є «Теореми безкоштовно» Вадлера. Помітний вплив на дизайн мови? Тип IO, класи класів.


0

Ієрархія Кліні показує нам , що докази не є програмами.

Перше рекурсивне відношення є або:

R1( Program , Iteration )  Program halts at Iteration.
R2( Theorem , Proof ) Proof proves a Theorem.

Першими рекурсивно переліченими відносинами є:

(exists x) R1( Program , x )  Program Halts.
(exists x) R2( Theorem , x)   Theorem is provable.

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

Program = Theorem
Iteration = Proof

Коли програма правильно виготовлена ​​із специфікації, ми повинні бути в змозі довести, що вона відповідає специфікації, і якщо ми можемо довести, що програма задовольняє специфікації, то це правильний синтез програми. Отже, ми виконуємо синтез програми, якщо ми доводимо, що програма задовольняє специфікації. Теорема про те, що програма задовольняє специфікації, є програмою в тому, що теорема стосується програми, яка синтезується.

Помилкові висновки Мартіна Лофа ніколи не виробляли жодної комп'ютерної програми, і дивно, що люди вважають, що це методологія синтезу програм. Жодного повного прикладу ніколи не наводиться, що синтезується програма. Специфікація, така як "ввести тип і вивести програму такого типу", не є функцією. Таких програм існує декілька, і обирати одну випадково - це не рекурсивна функція або навіть функція. Це просто дурна спроба показати синтез програми з дурною програмою, яка не представляє реальної комп'ютерної програми, що обчислює рекурсивну функцію.


2
як ця відповідь на поставлене запитання: "якими помітними способами це твердження вплинуло на дизайн мови?"
gnat

1
@gnat - ця відповідь стосується основного припущення в первісному запитанні, а саме: " doesn't this really apply to pretty much all the programming languages?" Ця відповідь стверджує / показує, що припущення є недійсним, тому не має сенсу звертатися до решти питань, які ґрунтуються на хибній передумові .
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.