Интерпретер - это анти-паттерн?

Для меня паттерн интерпретатора звучит очень похоже на анти-паттерн, известный как десятое правило Гринспуна:

Любая достаточно сложная программа на C или Fortran содержит специальную, неформально определенную, медленную реализацию половины Common Lisp.

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

Или, в качестве альтернативы, встраивайте в ваше приложение хорошо известный и хорошо определенный язык, например Guile (встраиваемая схема GNU). Или используйте Haskell в качестве встроенного предметно-ориентированного языка.

Но я не видел этого на практике - каков ваш опыт создания собственных встроенных языков? Это хорошая идея? Это лучше, чем встраивание уже существующего языка?

(Я не особенно фанат LISP. Это хорошо, но так же, как C, Haskell, Python и многие другие языки.)

10 ответов

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

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

Любой шаблон дизайна - это анти-шаблон, если его неправильно использовать.

Хорошее использование шаблона интерпретатора:

  • Программный компилятор
  • Механизм оценки SQL
  • Графический калькулятор ввода парсера
  • Анализатор XML

Это все программы, которые решают проблему оценки слов на каком бы то ни было языке.

Имейте в виду, что "шаблон интерпретатора" - это особый шаблон проектирования в ООП.

Это не имеет ничего общего с "переводчиками" или их использованием в целом.

В какой-то момент проекту, вероятно, понадобится система конфигурации. сначала нужно просто что-то простое, например, пары ключ-значение, чтобы система могла подключиться к локальной базе данных, или что-то подобное. Если это все, что нужно, обычно просто взломать простой парсер для диалекта INI или CSV и двигаться дальше. Но по мере роста и развития системы все большее значение приобретает конфигурация. Это нормально и полезно, рефакторинг функциональности и ответственность за правильный уровень абстракции. Начиная с этого момента разработчикам или даже пользователям (задыхающимся) понадобится более выразительный язык конфигурации. Еще до того, как кто-то заметит, в систему встроен и используется полный язык тьюринга.

Это основная модель Гринспаннинга. Все было хорошо спроектировано до последней черты, когда специальный язык был внедрен в сферу реальной вычислительной работы.

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

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

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

Примером этого в дикой природе является django settings.py. По какой-то причине, django не очень умно угадывает, где установлен проект django. С помощью небольшого количества стандартного python в файле конфигурации это можно сделать в общем случае переносимым способом, который подойдет практически каждому возможному пользователю. Несмотря на это, большая часть файла settings.py выглядит как обычные старые пары ключ = значение, типичные для конфигурационных файлов в стиле.ini.

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

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

Шаблон интерпретатора больше относится к идее сохранения графов объектов, но к выбору постоянного формата, который удобочитаем и редактируем (например, 2 + 3 представляя Addition объект с указателями на пару Integer объекты). Даже если это никогда не показывается клиентам как "язык", это все равно помогает отладке, поддержке, взлому на месте и так далее. Другими распространенными примерами могут быть языки запросов, такие как HQL в [N]Hibernate - ни один из существующих языков не был бы столь же хорош для описания запроса на этом уровне абстракции.

Как и предполагали другие, XML обычно используется в качестве основного синтаксиса для этого (особенно при представлении сохраняемого графа объектов в качестве "файла конфигурации", а также в виде XAML от Microsoft), но на самом деле это неоптимальный выбор. JSON в UTF-8 (или аналогичный) будет чище, проще для анализа, более читабельным и редактируемым, и, вероятно, лучше для большинства приложений. Но есть несколько великолепных облегченных библиотек синтаксического анализатора, которые принимают описание синтаксиса, похожего на BNF, и могут затем анализировать текст в структуру, похожую на DOM, во время выполнения, поэтому создание высокоспецифичного сжатого синтаксиса не составляет проблем.

Вы, очевидно, не фанат Лиспа, потому что мальчикам и девочкам, которые соответствуют этому описанию, обычно можно полагать, что Лисп - это скомпилированный язык. Lisp появился в 1958 году. Руководство Lisp от 1961 года уже описывает компиляцию.

Интерпретация полезна; это дает нам семантику без необходимости сначала писать компилятор. Эта семантика обеспечивает эталонную модель для скомпилированной семантики: в идеале интерпретируемые и скомпилированные программы делают то же самое. Когда мы обнаруживаем, что они этого не делают, мы либо решаем это, либо каким-то образом очерчиваем и оправдываем ситуацию.

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

Реализация CLISP ANSI Common Lisp является хорошим примером этого. Для сборки CLISP вам нужен только компилятор Си. Lisp-компилятор CLISP написан на Lisp. Так что, конечно, вам нечем запускать этот компилятор. Решение состоит в том, чтобы интерпретировать компилятор, используя интерпретатор, написанный на C.

Во время работы с интерпретатором компилятор компилирует большую часть библиотеки Lisp. Результатом этого является то, что CLISP называет "полускомпилированным образом памяти": образ, который содержит скомпилированные подпрограммы, но некоторые интерпретированные подпрограммы. (Я думаю, что сам компилятор все еще интерпретирует код).

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

Без интерпретатора компилятор CLISP мог быть загружен только при условии, что сначала будет установлена ​​другая реализация Lisp. (То, как вам нужен компилятор C для усиления GCC). В противном случае компилятор CLISP должен был бы быть написан на C, чтобы компилятор компилировался с использованием существующего компилятора C, а затем применялся к коду Lisp для его компиляции, прежде чем он сможет быть запущен.

Никто в здравом уме не хочет писать компилятор Lisp на C, и требование реализации Lisp для построения реализации Lisp является большим недостатком в мире, в котором Lisp не является повсеместным. Что произойдет в модели Boostpping Lisp-compiler-in-C, так это то, что Lisp-компилятор, написанный на C, будет полностью минимальным и, возможно, неполным, который генерирует некачественный код, достаточно хороший, чтобы инициировать Boostrapping, и "реальный" Компилятор все равно будет написан на Лиспе.

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

Похоже, вы пытаетесь создать новую городскую легенду? Вы враждебно настроены против доменных языков?

Интерпретатор - это идея генератора синтаксического анализатора JavaCC. Я думаю, что это работает отлично.

Интерпретатор - гораздо более респектабельный паттерн GoF, чем Singleton. Это тот, за которого нужно проголосовать за остров. Может быть, и память.

Возможно, реализация компилятора Lisp включает пример шаблона интерпретатора.

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

Обычно фазы компилятора / интерпретатора выглядят так:

  1. Синтаксис
  2. Разбор дерева
  3. АСТ
  4. Компилировать или интерпретировать

В Лиспе 1 и 2 объединены в 3.

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

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

Другие вопросы по тегам