Напишіть перекладача Haskell у Haskell


90

Класичною вправою програмування є написання перекладача Lisp / Scheme у Lisp / Scheme. Потужність повної мови може бути використана для створення перекладача для підмножини мови.

Чи існує подібна вправа для Хаскелла? Я хотів би реалізувати підмножину Haskell, використовуючи Haskell як движок. Звичайно, це можна зробити, але чи є в мережі доступні будь-які ресурси?


Ось попередня історія.

Я досліджую ідею використання Haskell як мови для вивчення деяких концепцій курсу дискретних структур, який я викладаю. У цьому семестрі я зупинився на Міранді , меншій мові, яка надихнула Хаскелла. Міранда робить близько 90% того, що я хотів би, але Хаскелл - близько 2000%. :)

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

Педагогічні "мовні рівні" успішно використовуються для навчання Java та Scheme . Обмежуючи те, що вони можуть зробити, ви можете перешкодити їм стріляти собі в ногу, поки вони все ще опановують синтаксис та поняття, яким ви намагаєтесь навчити. І ви можете запропонувати кращі повідомлення про помилки.


У мене є діалект WIP Haskell, реалізований із введенням Haskell в Haskell як основу. Тут є демо-версія цього chrisdone.com/toys/duet-delta Він не готовий до загальнодоступного випуску з відкритим кодом, але я міг би поділитися з вами джерелом, якщо зацікавлений.
Christopher Здійснено

Відповіді:


76

Я люблю вашу мету, але це велика робота. Кілька підказок:

  • Я працював над GHC, і ви не хочете жодної частини джерел. Обійми - це набагато простіша, чистіша реалізація, але, на жаль, це в C.

  • Це невеликий шматочок головоломки, але Марк Джонс написав прекрасну статтю під назвою Typing Haskell в Haskell, яка стане чудовою відправною точкою для вашого фронту.

Удачі! Визначення мовних рівнів для Хаскелла з підтвердженням доказів із класу мало б велику користь для громади та, безумовно, опублікований результат!


2
Цікаво, чи коментар щодо GHC все ще є точним. GHC є складним, але він досить добре задокументований. Зокрема, внутрішні Notesдані корисні для розуміння деталей низького рівня, а глава про GHC в Архітектурі програм з відкритим кодом пропонує чудовий огляд високого рівня.
sjy

37

Існує повний парсер Haskell: http://hackage.haskell.org/package/haskell-src-exts

Після того, як ви проаналізували його, видалити чи заборонити певні речі легко. Я зробив це для tryhaskell.org, щоб заборонити оператори імпорту, підтримати визначення верхнього рівня тощо.

Просто проаналізуйте модуль:

parseModule :: String -> ParseResult Module

Тоді у вас є AST для модуля:

Module SrcLoc ModuleName [ModulePragma] (Maybe WarningText) (Maybe [ExportSpec]) [ImportDecl] [Decl]    

Тип Decl є великим: http://hackage.haskell.org/packages/archive/haskell-src-exts/1.9.0/doc/html/Language-Haskell-Exts-Syntax.html#t%3ADecl

Все, що вам потрібно зробити, це визначити білий список - про те, які декларації, імпорти, символи, синтаксис доступні, потім пройдіть AST і викиньте "помилку синтаксичного аналізу" на все, про що ви ще не хочете, щоб вони були в курсі. Ви можете використовувати значення SrcLoc, прикріплене до кожного вузла в AST:

data SrcLoc = SrcLoc
     { srcFilename :: String
     , srcLine :: Int
     , srcColumn :: Int
     }

Не потрібно повторно впроваджувати Haskell. Якщо ви хочете надати більш дружні помилки компіляції, просто проаналізуйте код, відфільтруйте його, надішліть компілятору та проаналізуйте результати компілятора. Якщо це "не вдалося збігатись із очікуваним типом a проти виведеного a -> b", то ви знаєте, що це, мабуть, занадто мало аргументів для функції.

Якщо ви насправді не хочете витратити час на впровадження Haskell з нуля або возитися з внутрішніми елементами Hugs, або на якусь тупу реалізацію, я думаю, вам слід просто відфільтрувати те, що передається GHC. Таким чином, якщо ваші студенти хочуть взяти свою базу коду і перейти до наступного кроку і написати якийсь справжній повноцінний код Хаскелла, перехід буде прозорим.


24

Ви хочете створити свого перекладача з нуля? Почніть із впровадження простішої функціональної мови, такої як лямбда-числення або варіант lisp. Для останнього існує досить приємна вікікнига під назвою Напишіть собі схему за 48 годин дає прохолодне та прагматичне введення в методи розбору та інтерпретації.

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

Отже, вам слід визначити досить невелику підмножину Haskell, з якою можна працювати, а потім, можливо, почати з покрокового розширення прикладу Scheme.

Додаток:

Зауважте, що в Haskell ви маєте повний доступ до API інтерпретаторів (принаймні згідно з GHC), включаючи аналізатори, компілятори та, звичайно, інтерпретатори.

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


12
Зверніть увагу, що висновок типу - це насправді дуже простий алгоритм 20-30 рядків. це красиво своєю простотою. Ледачу оцінку також не так важко закодувати. Я б сказав, що складність полягає в божевільному синтаксисі, узгодженні шаблонів і просто великій кількості матеріалів на мові.
Клаудіу,

Цікаво - Чи можете ви розміщувати посилання на алгоритми введення тексту?
Даріо

5
Так, перегляньте цю безкоштовну книгу - cs.brown.edu/~sk/Publications/Books/ProgLangs/2007-04-26 -, це на сторінці 273 (289 pdf). Псевдокод alg знаходиться на P296.
Клавдіу,

1
У " Впровадженні функціональних мов програмування " також є реалізація (?) Алгоритму виведення / перевірки типу .
Філ Армстронг,

1
Однак висновок про тип з класами типів не є простим.
Christopher Здійснено

20

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

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

Це було б схоже на HLint (а також свого роду протилежність):

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

  • Впровадьте власні "пропозиції" HLint, щоб не використовувати функції, які ви не дозволяєте
  • Вимкніть усі стандартні пропозиції HLint.
  • Першим кроком зробіть так, щоб ваша обгортка запускала модифікований HLint
  • Ставтеся до пропозицій HLint як до помилок. Тобто, якщо HLint "скаржився", програма не переходить до етапу компіляції


6

Серія компіляторів EHC, мабуть, найкращий вибір: вона активно розробляється і, здається, саме те, що ви хочете - серія невеликих компіляторів / інтерпретаторів лямбда-чисел, кульмінацією яких став Хаскелл '98.

Але ви також можете поглянути на різні мови, розроблені в " Типах та мовах програмування" Пірса , або гелієвий інтерпретатор (скалічений Haskell, призначений для студентів http://en.wikipedia.org/wiki/Helium_(Haskell) ).


6

Якщо ви шукаєте підмножину Haskell, яку легко реалізувати, ви можете позбутися класів типів і перевірки типу. Без класів типів вам не потрібен умовивід типу для оцінки коду Хаскелла.

Я написав самокомпілюючий компілятор підмножин Haskell для завдання Code Golf. Він приймає код підмножини Хаскелла на вході і виробляє код С на виході. Мені шкода, що немає доступнішої для читання версії; Я піднімав вкладені визначення вручну в процесі самостійного складання.

Для студента, зацікавленого в застосуванні перекладача для підмножини Haskell, я б рекомендував почати з наступних функцій:

  • Ледача оцінка. Якщо перекладач знаходиться в Haskell, вам, можливо, нічого для цього робити не доведеться.

  • Визначення функцій із аргументами та захисниками, що відповідають шаблону. Турбуйтеся лише про змінні, мінуси, нуль та _закономірності.

  • Синтаксис простого виразу:

    • Цілочисельні літерали

    • Буквальні символи

    • [] (нуль)

    • Застосування функції (ліворуч асоціативно)

    • Інфікс :(мінуси, права асоціативна)

    • Дужки

    • Назви змінних

    • Назви функцій

Більш конкретно, напишіть інтерпретатор, який може запустити це:

-- tail :: [a] -> [a]
tail (_:xs) = xs

-- append :: [a] -> [a] -> [a]
append []     ys = ys
append (x:xs) ys = x : append xs ys

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
zipWith _ _      _      = []

-- showList :: (a -> String) -> [a] -> String
showList _    []     = '[' : ']' : []
showList show (x:xs) = '[' : append (show x) (showItems show xs)

-- showItems :: (a -> String) -> [a] -> String
showItems show []     = ']' : []
showItems show (x:xs) = ',' : append (show x) (showItems show xs)

-- fibs :: [Int]
fibs = 0 : 1 : zipWith add fibs (tail fibs)

-- main :: String
main = showList showInt (take 40 fibs)

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


"Ледача оцінка. Якщо перекладач перебуває в Haskell, можливо, вам не доведеться для цього нічого робити". Це може бути неправдою. Дивіться статтю Нейлора на Haskell.org/wikiupload/0/0a/TMR-Issue10.pdf для отримання додаткової інформації про впровадження ледачого перекладача в Haskell.
Джаред Апдайк


3

Це може бути непоганою ідеєю - створіть крихітну версію NetLogo у Haskell. Ось крихітний перекладач.


Посилання мертві. Є шанс, що цей вміст все ще існує десь ще? Мені було б цікаво ...
Ніколас Пайєт

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

1
Пошук у Google за запитом "netlogo haskell" виявляє ... це питання. У всякому разі, нічого страшного. Дякую!
Ніколас Пайєтт,



2

Мені сказали, що Ідріс має досить компактний синтаксичний аналізатор, не впевнений, чи справді він підходить для модифікації, але це написано в Haskell.


2

Зоопарк мови програмування Андрея Бауера має невелику реалізацію чисто функціональної мови програмування, дещо нахабно названу "міні-хаскелл". Це близько 700 ліній OCaml, так дуже легко засвоюваних.

Сайт також містить іграшкові версії мов програмування в стилі ML, Prolog та OO.


1

Чи не вважаєте ви, що було б простіше взяти джерела GHC і вилучити те, чого не хочете, ніж було б написати власний перекладач Haskell з нуля? Взагалі кажучи, для вилучення потрібно докладати набагато менше зусиль об’єктів ніж на створення / додавання об’єктів.

GHC у будь-якому випадку написаний на Haskell, тому технічно це відповідає вашим питанням про перекладача Haskell, написаному на Haskell.

Можливо, не буде надто складно зробити все це статично пов’язаним, а потім розповсюджувати лише власні налаштовані GHCi, щоб студенти не могли завантажувати інші вихідні модулі Haskell. Щодо того, скільки роботи знадобиться, щоб вони не могли завантажувати інші об’єктні файли Haskell, я поняття не маю. Можливо, ви захочете також відключити FFI, якщо у вас є маса шахраїв у ваших класах :)


1
Це не так просто, як здається, оскільки багато функцій залежать від інших. Але можливо OP просто хоче не імпортувати Prelude, а натомість надати власний. Більшість Haskell, які ви бачите, є звичайними функціями, а не специфічними особливостями середовища виконання. (Але, звичайно, багато є .)
jrockway

0

Причина, по якій існує так багато інтерпретаторів LISP, полягає в тому, що LISP в основному є попередником JSON: простий формат для кодування даних. Це робить фасадну частину досить простою в обробці. Порівняно з цим, Haskell, особливо з розширенням мови, не є найпростішою мовою для аналізу. Ось деякі синтаксичні конструкції, які звучать хитро, щоб отримати правильне:

  • оператори з настроюваним пріоритетом, асоціативністю та фіксованістю,
  • вкладені коментарі
  • правило макета
  • синтаксис шаблону
  • do- блокування та знебарвлення до монадичного коду

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

Я пропоную застосувати перевірку типу та інтерпретатор Coreзамість повного Haskell. Обидва ці завдання вже самі по собі досить складні. З цією мовою, хоча вона все ще сильно набрана функціональна мова, набагато менш складною є робота з точки зору оптимізації та генерації коду. Однак він все ще незалежний від основної машини. Тому GHC використовує його як мову-посередник і перекладає на нього більшість синтаксичних конструкцій Хаскелла.

Крім того, ви не повинні уникати використання інтерфейсу GHC (або іншого компілятора). Я б не вважав це обманом, оскільки користувацькі LISP використовують парсер системи LISP хоста (принаймні під час завантаження). Очищення Coreфрагментів та представлення їх студентам, разом із оригінальним кодом, має дозволити вам дати огляд того, що робить інтерфейс, і чому переважно не застосовувати його.

Ось кілька посилань на документацію Core, яка використовується в GHC:

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