12-07-2023
Предметно-ориентированный язык (англ. 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], а также «языками, реализованными поверх или на основе данного языка».
В дополнение к традиционному делению языков на интерпретируемые и компилируемые, встраиваемые языки вводят ещё несколько видов реализации языка:
С другой стороны, можно рассматривать реализацию встраиваемого языка как «реализацию без трансляции», подразумевая, что DSL будет являться синтаксическим и семантическим подмножеством языка, в который он встраивается[9].
Язык, используемый в качестве базового для реализации другого, часто называют метаязыком.
Основных причин для разработки встраиваемых текстовых языков три:
Наиболее частыми примерами языков первой группы могут служить реализации объектно-ориентированных возможностей в функциональных[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, которые, в свою очередь, реализуются различным образом в зависимости от ОС.
Один из языков (базовый или встраиваемый) может быть визуальным, что нередко применяется в пользовательском программировании[en] (англ. End-user development). Типичными примерами таких пар могут служить AutoLisp — AutoCAD и VBA — Microsoft Excel. Подобные пары образуют целостную интерактивную систему, и с точки зрения пользователя невозможно (и не нужно) определить, являются ли визуальные инструменты надстройкой, имитирующей команды встроенного текстового языка, или же текстовый язык управляет визуальными инструментами. Действительные взаимоотношения в этих парах определяются разработчиком.
В паре Emacs — Emacs Lisp отношения более определённые. Лисп традиционно относится к метаязыкам, и в данном случае текстовый редактор надстраивается над ним как визуальный DSL, что и делает последний изменяемым и расширяемым.
В случае, когда оба языка являются визуальными, встраиваемые языки обычно называют иными терминами — плагинами, фильтрами и др., и не используют терминологию языково-ориентированного программирования. Формально же можно говорить, например, что для визуального мета-языка обработки графики Adobe Photoshop есть множество встраиваемых визуальных мини-языков (см. Photoshop plugin[en]).
Функциональные и логические языки программирования выглядят неестественно в визуальном окружении, т.к. ФП и ЛП в чистом виде запрещает побочные эффекты, и для взаимодействия с GUI их концептуальную целостность приходится нарушать. С педагогической т.з. считается желательным преподавание программирования с использованием консольных средств, чтобы сосредоточить внимание студентов на основах алгоритмизации, а не эргономики и тем более не процедурных навыков использования тех или иных IDE[16].
Преимущества и недостатки использования в конкретной задаче конкретного DSL вместо языка общего назначения определяются гораздо явственнее, чем преимущества и недостатки использования одного языка общего назначения вместо другого: в большинстве случаев уже разработанный DSL оказывается концептуально неприменим к одним задачам и даёт бесспорный выигрыш по большинству показателей качества в других, а некоторые подзадачи вообще остаются нерешёнными до разработки DSL[3].
Таким образом, вопрос о преимуществах и недостатках корректнее ставить в свете применения языково-ориентированной методологии вместо какой-либо другой при изначальном отсутствии готового DSL, сопоставляя потенциальный выигрыш от его использования с затратами на его разработку и сопровождение.
Мини-язык.