Почему проще написать компилятор на функциональном языке?

Я очень долго думал над этим вопросом, но действительно не смог найти ответ в Google, а также аналогичный вопрос в Stackoverflow. Если есть дубликат, извините за это.

Кажется, многие люди говорят, что писать компиляторы и другие языковые инструменты на функциональных языках, таких как OCaml и Haskell, гораздо эффективнее и проще, чем писать их на императивных языках.

Это правда? И если да, то почему так эффективно и легко писать их на функциональных языках, а не на императивном языке, таком как C? Кроме того, разве языковой инструмент на функциональном языке не медленнее, чем на каком-нибудь низкоуровневом языке, таком как C?


person wvd    schedule 25.05.2010    source источник
comment
Я бы не сказал, что легче. Но функциональная природа задач компиляции, таких как синтаксический анализ, вероятно, вполне естественно подходит для функционального программирования. Функциональные языки, такие как OCaml, могут быть очень быстрыми, соперничая со скоростью C.   -  person Robert Harvey    schedule 25.05.2010
comment
Люди, это действительно аргумент? Наверняка у кого-то есть понимание. Я хотел бы знать себя.   -  person Robert Harvey    schedule 25.05.2010
comment
Я думаю, что должно быть по крайней мере несколько веских причин, по которым следует использовать функциональный язык вместо императивного. Я нашел какую-то статью, которая в основном сводилась к тому, что функциональные языки не имеют побочных эффектов и тому подобное. Но это было не совсем ясно. Однако, если это спорно, то, возможно, было бы лучше закрыть его или переформулировать вопрос.   -  person wvd    schedule 25.05.2010
comment
Можем ли мы изменить его на «…эффективно и легко»? Это делает его менее спорным.   -  person Donal Fellows    schedule 25.05.2010
comment
Действительно ли аргументировано признать, что некоторые ниши лучше подходят для определенного стиля языка? Я думаю, что вопрос о том, почему C лучше, чем Javascript для написания драйверов устройств, не вызывает споров...   -  person C. A. McCann    schedule 25.05.2010
comment
Я думал, что будет наоборот. Я читаю супер крошечный компилятор, и он повсюду использует переменную мутацию.   -  person Rolf    schedule 25.09.2017


Ответы (7)


Часто компилятор много работает с деревьями. Исходный код анализируется в синтаксическом дереве. Затем это дерево может быть преобразовано в другое дерево с аннотациями типов для выполнения проверки типов. Теперь вы можете преобразовать это дерево в дерево, содержащее только основные элементы языка (преобразовав синтаксические сахароподобные нотации в несахарную форму). Теперь вы можете выполнять различные оптимизации, которые в основном представляют собой преобразования дерева. После этого вы, вероятно, создадите дерево в какой-то нормальной форме, а затем выполните итерацию по этому дереву, чтобы создать целевой (ассемблерный) код.

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

person sepp2k    schedule 25.05.2010
comment
Самый полный ответ на данный момент, я отмечу это как принятый ответ, однако я думаю, что ответ Пита Киркхэма также хорош. - person wvd; 25.05.2010
comment
Что касается доказательства правильности, поскольку корректность компилятора является важным атрибутом, я часто слышал, что любители функциональных языков каким-то образом включают доказательство правильности в свой рабочий процесс. Я понятия не имею, что это на самом деле означает с практической точки зрения, но поскольку надежность компилятора важна, это кажется стоящим. - person Warren P; 25.05.2010
comment
@WarrenP: Концепция кода, несущего доказательства, исходит из функциональных языков со статической типизацией. Идея состоит в том, что вы используете систему типов таким образом, чтобы функция могла только проверять правильность типов, поэтому факт компиляции кода является доказательством правильности. Конечно, это не полностью возможно, сохраняя язык тьюринг-полным и разрешимым при проверке типов. Но чем сильнее система типов, тем ближе вы можете приблизиться к этой цели. - person sepp2k; 25.05.2010
comment
Причина, по которой эта концепция в основном популярна в функциональном сообществе, заключается в том, что в языках с изменяемым состоянием вам также придется кодировать информацию о том, когда и где происходит изменение состояния в типах. В языках, где вы знаете, что результат функции зависит только от ее аргументов, гораздо проще кодировать доказательство в типах (также гораздо проще вручную проверять правильность кода, потому что вам не нужно учитывать, какое глобальное состояние является возможно и как это повлияет на поведение функции). Однако ничего из этого конкретно не связано с компиляторами. - person sepp2k; 25.05.2010
comment
@Warren P: Обычно это означает математическую спецификацию того, что должна делать программа, и доказательство того, что программа действительно это делает. В терминах компилятора это будет означать что-то вроде этого определения исходного языка, выходной код будет выполнять те же вычисления, и любые оптимизации изменят только использование времени/пространства программой, а не ее поведение. - person C. A. McCann; 25.05.2010
comment
Единственной наиболее важной функцией, на мой взгляд, является сопоставление с образцом. Оптимизировать абстрактное синтаксическое дерево с помощью сопоставления с образцом очень просто. Делать это без сопоставления с образцом часто очень сложно. - person Bob Aman; 25.05.2010
comment
Таким образом, вы можете обобщить ответ: компилятор — это функция из предложения на одном языке в предложение на другом языке (с сохранением некоторого семантического сопоставления значений обоих языков). Если вы рассматриваете свой компилятор как функцию, разумно обратиться к функциональному программированию для поддержки его реализации... - person Dafydd Rees; 23.04.2011

Многие задачи компилятора связаны с сопоставлением шаблонов в древовидных структурах.

И OCaml, и Haskell обладают мощными и лаконичными возможностями сопоставления с образцом.

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

person Pete Kirkham    schedule 25.05.2010
comment
Звучит как разумный ответ, но только ли это? например будут ли играть роль такие вещи, как хвостовая рекурсия? - person wvd; 25.05.2010
comment
Казалось бы, это указывает на то, что проблема скорее связана с системой типов, чем с фактической моделью выполнения. Что-то, основанное на императивном программировании с неизменяемыми значениями по структурным типам, может подойти. - person Donal Fellows; 25.05.2010
comment
@wvd: оптимизация хвостовой рекурсии - это деталь реализации, а не функция языка как таковая, которая делает линейные рекурсивные функции эквивалентными итеративному циклу. Рекурсивная функция для обхода связанного списка в C выиграет от этого так же, как рекурсия по списку в Scheme. - person C. A. McCann; 25.05.2010
comment
@wvd gcc C имеет устранение хвостовых вызовов, как и другие языки с изменяемым состоянием. - person Pete Kirkham; 25.05.2010
comment
@wvd: я бы сказал нет о хвостовой рекурсии, в конце концов, это всего лишь оптимизация, и многие лиспы отлично подходят для написания компиляторов, и только часть из них обещает совокупную стоимость владения. - person Ukko; 25.05.2010
comment
Как показывает проект JMatch (cs.cornell.edu/Projects/jmatch), нет ничего невозможного в том, чтобы добавить мощное сопоставление с образцом к совершенно нефункциональному языку, такому как java. Однако фактом является то, что в настоящее время единственные распространенные языки, в которых есть сопоставление с образцом, являются (по крайней мере, частично) функциональными по своей природе. - person sepp2k; 25.05.2010
comment
@camccann: Если стандарт языка гарантирует tco (или, по крайней мере, гарантирует, что рекурсивные функции определенной формы никогда не вызовут переполнения стека или линейного роста потребления памяти), я бы счел это особенностью языка. Если стандарт этого не гарантирует, но компилятор все равно это делает, то это функция компилятора. - person sepp2k; 25.05.2010
comment
@sepp2k: sepp2k: Это странный случай, потому что это оптимизация, которая почти необходима компилятору функционального языка для получения полезного вывода. Спецификации языка иногда диктуют детали реализации или ограничения производительности по разным причинам, но мне сложно назвать такие вещи особенностями самого языка. Впрочем, ничего, кроме споров о терминологии, так что не обращайте на меня внимания! - person C. A. McCann; 25.05.2010

Один важный фактор, который следует учитывать, заключается в том, что большая часть любого проекта компилятора — это когда вы можете самостоятельно разместить компилятор и «есть свою собачью еду». По этой причине, когда вы смотрите на такие языки, как OCaml, где они предназначены для языковых исследований, они, как правило, имеют отличные возможности для решения проблем типа компилятора.

В моей последней работе, похожей на компилятор, мы использовали OCaml именно по этой причине при манипулировании кодом C, это был просто лучший инструмент для этой задачи. Если бы люди из INRIA построили OCaml с другими приоритетами, возможно, он не был бы таким подходящим.

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

/me: чуть менее радостно ползет обратно к моим Java-задачам...

person Ukko    schedule 25.05.2010
comment
-1 для функциональных языков — лучший инструмент для решения любой задачи. Если бы это было правдой, мы бы использовали их повсюду. ;) - person Andrei Krotkov; 26.05.2010
comment
@Andrei Krotkov: Сегодняшнее слово дня - fa·ce·tious Произношение: \fə-ˈsē-shəs\ Функция: прилагательное Этимология: среднефранцузское facetieux, от facetie jest, от латинского facetia Дата: 1599 1 : шутит или шутит часто неуместно : waggish ‹просто шутка› 2 : должно быть смешно или забавно : несерьезно ‹шутливое замечание› синонимы см. witty Помимо отсутствия шутки, ваша логика все еще ошибочна. Вы полагаете, что все люди — рациональные действующие лица, и боюсь, это несправедливое предположение. - person Ukko; 26.05.2010
comment
Думаю, я пропустил шутку, так как знаю людей в реальной жизни, которые сказали бы почти то же самое, но совершенно серьезно. Закон По, наверное. tvtropes.org/pmwiki/pmwiki.php/Main/PoesLaw - person Andrei Krotkov; 27.05.2010
comment
@Andrei: Используя свой argumentum ad populum: если разум лучше эмоционального невежества, мы все будем использовать его повсюду. - person Tim Schaeffer; 28.05.2010

По сути, компилятор — это преобразование одного набора кода в другой — от исходного кода к IR, от IR к оптимизированному IR, от IR к ассемблеру и т. д. Это как раз то, для чего предназначены функциональные языки — чистая функция просто превращение одного в другое. Императивные функции не обладают этим качеством. Хотя вы можете написать такой код на императивном языке, функциональные языки предназначены для этого.

person Chuck    schedule 25.05.2010

Одна из возможностей состоит в том, что компилятору приходится очень тщательно обрабатывать множество крайних случаев. Получение правильного кода часто упрощается за счет использования шаблонов проектирования, которые структурируют реализацию таким образом, чтобы она соответствовала правилам, которые она реализует. Часто это оказывается декларативным (сопоставление с образцом, «где»), а не императивным (последовательность, «когда») дизайном, и поэтому его легче реализовать на декларативном языке (и большинство из них функциональны).

person BCS    schedule 25.05.2010

Смотрите также

Шаблон проектирования F#

FP группирует вещи «по операциям», тогда как OO группирует вещи «по типам», а «по операциям» более естественно для компилятора/интерпретатора.

person Brian    schedule 25.05.2010
comment
Это относится к тому, что в некоторых кругах теории языков программирования называется проблемой выражения. Например, см. этот вопрос, в котором я демонстрирую действительно ужасный код на Haskell, который делает вещи с помощью расширяемых типов. Наоборот, принуждение языка ООП к расширяемому стилю операций, как правило, мотивирует шаблон посетителя. - person C. A. McCann; 25.05.2010

Похоже, все упустили еще одну важную причину. Довольно легко написать встроенный предметно-ориентированный язык (EDSL) для синтаксических анализаторов, который очень похож на (E)BNF в обычном коде. Комбинаторы синтаксического анализатора, такие как Parsec, довольно легко написать на функциональных языках, используя функции высшего порядка и композицию функций. Не только проще, но и очень элегантно.

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

Это, конечно, не единственный способ создания парных фреймворков.

person snk_kid    schedule 25.05.2010