Предметно-ориентированный язык

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Предметно-ориентированный язык (англ. Domain Specific language, DSL — «предметно-специфичный язык») — язык программирования, специализированный для конкретной области применения (в противоположность языку общего назначения[en], применимому к широкому спектру областей и не учитывающему особенности конкретных сфер знаний). Является ключевым понятием языково-ориентированного программирования.

Строго говоря, деление языков программирования на языки общего назначения и предметно-ориентированные весьма условно, особенно, если учесть, что формально любой протокол или формат файлов является языком. Существует масса языков общего назначения, применяемых в качестве предметно-ориентированных для определённых задач, и наоборот, предметно-ориентированных языков, применяемых в качестве языков общего назначения. Ярким примером является язык Си, разработанный в качестве кроссплатформенного ассемблера, но на практике применяемый гораздо шире. Язык ML, породивший целое семейство языков общего назначения (включая Haskell, ныне наиболее предпочитаемый разработчиками предметно-ориентированных языков в качестве базового), — изначально разрабатывался в качестве DSL для системы автоматического доказательства теорем LCF[en]. Примером, показывающим условность классификации, служит язык БНФ (и компилятор с него Lex/Yacc): с одной стороны, это яркий пример метаязыка, с другой — он предназначен для одной конкретной задачи.

Терминология[править | править вики-текст]

Простейшие предметно-ориентированные языки, используемые в одном конкретном приложении, часто называют «мини-языками»[1].

Мартин Уорд (англ. Martin Ward)[2] в работе «Language Oriented Programming»[3] (которая считается отправной точкой развития ЯОП), использовал термины «problem oriented» и «domain oriented», но в англоязычном научном сообществе прижился термин «domain specific», причём именно «domain specific language», а не «domain specific programming language». В русскоязычной литературе по программированию встречаются варианты «проблемно-специфичный», «предметно-ориентированный», «проблемно-ориентированный» и т. д. Семантически правильным переводом устоявшегося английского термина «domain specific» является вариант «предметно-специфичный».

Фаулер[4] и Дмитриев[5] определяют понятие DSL как «урезанный язык программирования (в большинстве случаев неполный по Тьюрингу)».

Примеры[править | править вики-текст]

Ведущие исследователи языково-ориентированного программирования (Мартин Уорд, Пол Хьюдак[en], Валид Таха и другие) приводят следующие примеры предметно-специфичных языков в качестве классических[3][6][7]:

По мнению Валида Тахи, с позиции ЯОП Microsoft Excel оказывается едва ли не наиболее широко применяемым в мире языком программирования[7].

Другими примерами предметно-ориентированных языков служат FoxPro, командные языки операционных систем (языки пакетных заданий, такие как JCL, языки интерактивной командной оболочки, такие как bash и batch), неполные по Тьюрингу языки структурирования данных (XML, .ini, .conf), язык Вики-разметки, языки моделирования (UML, GPSS), Erlang для многопользовательских серверов, функционирующих в бесперебойном режиме. Следует отметить, что примеры не всегда являются показательными, некоторые предметно-ориентированные языки подвергаются критике.

Встраиваемые языки[править | править вики-текст]

Временами компьютерные языки реализуются зависимым образом, т.е. «внутри» транслируемого языка, без которого эти языки не только не способны исполняться, но и зачастую не образуют целостную символьную систему и не обладают Тьюринг-полнотой. Такие языки называются «встраиваемыми предметно-специфичными языками» (англ. Embedded DSL, EDSL; иногда DSEL) или просто «встраиваемыми языками» (Embedded language)[6][8], а также «языками, реализованными поверх или на основе данного языка».

Текстовые языки[править | править вики-текст]

В дополнение к традиционному делению языков на интерпретируемые и компилируемые, встраиваемые языки вводят ещё несколько видов реализации языка:

  • чистое встраивание[6];
  • использование макросредств языка (и нередко отождествление их с термином «метапрограммирование»), которое, в свою очередь, подразделяется на[9]:
    • многостадийные вычисления (англ. multistage computations);
    • квазицитирование (известное из языка Lisp);
    • использование шаблонов.

С другой стороны, можно рассматривать реализацию встраиваемого языка как «реализацию без трансляции», подразумевая, что DSL будет являться синтаксическим и семантическим подмножеством языка, в который он встраивается[9].

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

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

  • ввод в исходный язык дополнительных возможностей, расширяющих спектр эффективно решаемых задач или синтаксически упрощающих решение часто встречаемых задач;
  • интенсивное ре-использование компонентов транслятора базового языка: парсера, механизма типизации, реализации тривиальных вещей (таких как арифметика чисел), оптимизатора и др. Это обеспечивает кратное снижение трудоёмкости реализации придуманного языка, а также высокий уровень качества реализации при использовании безопасного[en] языка в качестве базового;
  • получение возможности эффективно смешивать свойства разных самостоятельных языков в единых фрагментах кода, формируя мультипарадигменный язык широкого профиля, исключая необходимость межъязыкового взаимодействия[en] и расширяя возможности оптимизации.

Наиболее частыми примерами языков первой группы могут служить реализации объектно-ориентированных возможностей в функциональных[10] или процедурных[11] языках, и классическим примером служит язык CLOS. Следует отметить, что термин «язык» здесь используется не всегда — временами говорят просто о «реализации в языке новых возможностей» или о «расширении языка подсистемой, нацеленной на решение определённых задач», и нет строго деления на «библиотеки» и «встраиваемые языки», т.к. формально любой API, протокол или структура данных может рассматриваться в качестве языка[12]. Так, например, неотъемлемой частью языка Lisp является встроенный не полный по Тьюрингу язык S-выражений.

Вторая группа встраиваемых языков наиболее полно представлена в сообществе языка Haskell, и потому сам Haskell временами определяют как «DSL для денотационной семантики»[6]. Примерами могут служить Elm[en] и другие языки, представляющие функциональную реактивную парадигму, а также язык Curry. Временами также встречается похожее выражение в отношении Лиспа: «Lisp — это не язык, а среда для разработки языков». Примером языка, реализованного поверх Лиспа, может служить Qi[en]. Масса встраиваемых мини-языков реализована в языке OCaml посредством модуля CamlpX[en] компилятора. Язык Rebol также проектировался для программирования посредством интенсивной реализации встраиваемых мини-языков. В диалекте Лиспа Scheme посредством языка S-выражений реализован не полный по Тьюрингу язык SXML[en], воплощающий протокол XML встраиваемым образом.

Встраиваемый язык может иметь самодостаточную полную по Тьюрингу семантику, но тем не менее вместо независимой реализации ре-использовать компоненты базового языка (третья группа, смешение первых двух). Ярким примером является язык Schelog[13], реализующий семантику Пролога внутри диалекта Лиспа Scheme посредством продолжений, и превращающий Пролог из «самостоятельного» языка во встраиваемый. Традиционной учебной или «спортивной» задачей для многих функциональных языков служит реализация поверх рассматриваемого языка какого-либо другого, чаще всего языка логики предикатов первого порядка[14].

В контексте метаязыков самостоятельные языки временами называют «языками первого класса» (по аналогии с сущностями первого класса в языках), а встраиваемые — «объектными языками».

В подавляющем большинстве случаев встраиваемые языки имеют лишь одну поддерживаемую реализацию, и различия в результирующем машинном представлении кода на них зависят лишь от используемого транслятора базового языка, однако, бывают и исключения. Например, язык Concurrent ML[en] (CML), расширяющий Standard ML конструкциями для явного параллелизма, имеет две принципиально различные реализации, что обусловлено различиями в стратегиях компиляции, применяемых разными реализациями самого Standard ML. В компиляторе SML/NJ язык CML реализован посредством продолжений[15], и поскольку SML/NJ использует при компиляции стратегию «программирование в стиле передачи продолжений» (англ. Continuation-passing style, CPS), полностью устраняющую стек вызовов из машинного кода за счёт некоторых потерь на самих вызовах, то компиляция получается достаточно эффективной для адресуемых им задач, в 2-3 раза уступая по скорости языку Си. Как следствие, CML в SML/NJ компилируется одинаково под различными ОС, на которые портирован SML/NJ. В отличие от него, компилятор MLton использует значительно более агрессивную оптимизацию, изначально ориентируясь на скорость Си, и CPS-стратегия оказывается неприменима, но без неё каждый вызов callcc требует временных затрат, пропорциональных размеру стека, что в данном случае тем более неприемлемо. Поэтому при портировании CML на MLton он был реализован заново посредством собственных «тонких» потоков MLton[16], которые, в свою очередь, реализуются различным образом в зависимости от ОС[17].

Визуальные языки[править | править вики-текст]

Один из языков (базовый или встраиваемый) может быть визуальным, что нередко применяется в пользовательском программировании[en] (англ. End-user development). Типичными примерами таких пар могут служить AutoLispAutoCAD и VBAMicrosoft Excel. Подобные пары образуют целостную интерактивную систему, и с точки зрения пользователя невозможно (и не нужно) определить, являются ли визуальные инструменты надстройкой, имитирующей команды встроенного текстового языка, или же текстовый язык управляет визуальными инструментами. Действительные взаимоотношения в этих парах определяются разработчиком.

В паре EmacsEmacs Lisp отношения более определённые. Лисп традиционно относится к метаязыкам, и в данном случае текстовый редактор надстраивается над ним как визуальный DSL, что и делает последний изменяемым и расширяемым.

В случае, когда оба языка являются визуальными, встраиваемые языки обычно называют иными терминами — плагинами, фильтрами и др., и не используют терминологию языково-ориентированного программирования. Формально же можно говорить, например, что для визуального мета-языка обработки графики Adobe Photoshop есть множество встраиваемых визуальных мини-языков (см. Photoshop plugin[en]).

Функциональные и логические языки программирования выглядят неестественно в визуальном окружении, т.к. ФП и ЛП в чистом виде запрещает побочные эффекты, и для взаимодействия с GUI их концептуальную целостность приходится нарушать. С педагогической т.з. считается желательным преподавание программирования с использованием консольных средств, чтобы сосредоточить внимание студентов на основах алгоритмизации, а не эргономики и тем более не процедурных навыков использования тех или иных IDE[18].

Преимущества и недостатки[править | править вики-текст]

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

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

См. также[править | править вики-текст]

Ссылки[править | править вики-текст]

Примечания[править | править вики-текст]

  1. Bentley - Little languages, 1986
  2. Martin Ward's Homepage
  3. 1 2 3 Ward - Language Oriented Programming, 1994
  4. Фаулер Языковой инструментарий: новая жизнь языков предметной области. — 2005.
  5. Сергей Дмитриев (JetBrains) Языково-ориентированное программирование: следующая парадигма // = RSDN Magazine. — 2005.
  6. 1 2 3 4 Hudak - Modular Domain Specific Languages and Tools, 1998
  7. 1 2 Taha - Domain-Specific Languages, 2008
  8. Mernik - Formal and Practical Aspects of Domain-Specific Languages, 2012
  9. 1 2 Czarnecki, O’Donnell, Striegnitz, Taha - DSL implementation in metaocaml, template haskell, and C++, 2004
  10. Bernard Berthomieu OO Programming Styles in ML. — LAAS Report #2000111, Centre National De La Recherche Scientifique Laboratoire d'Analyse et d'Architecture des Systèmes, 2000.
  11. Cello — library that introduces higher level programming to C
  12. Хопкрофт, Мотвани, Ульман - Теория автоматов, языков и вычислений, 2001
  13. Schelog, 2003
  14. Paulson - ML for the Working Programmer, 1996
  15. Concurrent ML в SML/NJ
  16. Concurrent ML Implementation in MLton
  17. MLton Threads
  18. Игорь Головин, Андрей Столяров Мультипарадигмальный подход к преподаванию программирования и роль свободного ПО // МГУ им.Ломоносова, Тезисы докладов II конференции разработчиков свободных программ "На Протве". — г. Обнинск, 2005.

Литература[править | править вики-текст]