Pull to refresh

Как система JetBrains MPS позволяет достичь более широкого использование DSL-ей (языков специфичных для предметной области)

Website development *
DSL-и (domain specific languages или языки для специфичных областей) известны программистам давно. Несмотря на это, они редко используются в реальных системах. В этой статье будет рассмотрено, что такое DSL-и, и почему они не получили широкого распространения. Также будет описано, как система JetBrains MPS решает проблемы, препятствующие их широкому использованию.


Так что же такое DSL? DSL это язык, созданный для решения задач в определенной предметной области. DSL-ями являются большинство декларативных языков, которые решают задачки в узких предметных областях. Например, SQL, регулярные выражения, XPath, Prolog, формулы в Excel. К сожалению, на этом список широко используемых DSL-ей заканчивается. Основное достоинство таких языков в том, что благодаря близости их конструкций к предметной области, код на этих языках очень ясен и краток. Более того, чтобы редактировать код на таких языках, не обязательно быть программистом. Если человек разбирается в предметной области, то он легко может писать код на таких языках благодаря своим знаниям.

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

Давайте посмотрим на причины того, почему DSL-и не используются широко. Первая причина — авторы таких языков фокусируются на замкнутых языках только для одной конкретной проблемы, вместо того, чтобы расширять существующие языки программирования общего назначения, такие как Java, PHP, JavaScript. На это существует причина: возможность появления неоднозначностей при совмещении таких расширений друг с другом. Другая причина — сложность создания языковой инфраструктуры, необходимой для реализации языка, и комфортной работы с ним.

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

При создании расширений, для того, чтобы использовать их так же, как мы сейчас используем библиотеки, необходимо сделать языки совместимыми друг с другом. Это означает, что если мы добавляем в наш язык одно расширение, например, поддержку денег: тип для денег, литералы типа $10 или 100р, и другое расширение, которое добавляет в язык математические обозначения: суммы, произведения, итп, то мы сможем их использовать вместе, даже если они были созданы разными авторами.

К сожалению, все популярные языки программирования общего назначения основаны на текстовых грамматиках. У этих грамматик есть одно неприятное свойство: они могут быть неоднозначными, те возможно несколько интерпретаций одной и той же строки. Более того, если мы добавляем к Java новые конструкции при помощи расширения A, и добиваемся однозначности грамматики, а потом делаем то же самое с расширением B, может получиться так, что если мы возьмем Java и оба расширения, результирующая грамматика будет неоднозначной.

Давайте рассмотрим пример. Допустим, 2 компании решили добавить поддержку интерполяции строк (интерполяция строк позволяет писать выражения внутри строковых литералов) в Java. Допустим, первая компания использует такой синтаксис:
"{2+3}"
А вторая такой:
"${2+3}"
Если мы будем использовать оба расширения одновременно, и введем такую программу:
«Account balance is ${account.getBalance()}»
то ее интерпретация неоднозначна. Является ли $ частью синтаксиса интерполяции, или частью строкового литерала? Пример несколько искусственен, но позволяет понять общую проблему неоднозначности, которая возникает при наличии похожего синтаксиса для разных конструкций.

Чтобы добиться высокой производительности разработчика, необходимы интеллектуальные средства разработки. C появлением интеллектуальных редакторов, таких как в IntelliJ IDEA или в Eclipse, разработчикам бывает трудно переключится на редактирование текста в обычных редакторах. Текстовые редакторы не подсвечивают ошибки, не предоставляют контекстную помощь, не показывают меню с доступными вариантами, в них нет поддержки рефакторингов. Существуют фреймворки для создания интеллектуальных редакторов, например, IntelliJ IDEA Language API, XText, Oslo, но ни один из этих фреймворков не поддерживает расширяемые языки на должном уровне. Даже если нам не нужна расширяемость, создание поддержки языка с использованием этих средств требует хороших знаний в области языков программирования и занимает очень много времени. Как видно, инструментальная поддержка очень важна, но реализовать ее непросто.

Давайте подведем итог: люди занимаются не тем типом DSL-ей; для достижения увеличения производительности необходимо расширять существующие языки программирования общего назначения. Создавать же такие расширения сложно из-за того, что широко распространенные технологии не поддерживают совместимость расширений друг с другом.
Как JetBrains MPS решает указанные проблемы

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

MPS решает проблему неоднозначности радикальным способом: если у нас нет текстовой грамматики, то у нас нет и неоднозначности. Такой подход, однако, не означает, что в MPS не используются грамматики. Вместо конкретного синтаксиса, в определении языка в MPS определяется абстрактный синтаксис (структура синтаксического дерева). Если вы знакомы с XML, то наверное знаете об XML Schema, которая напоминает способ описания синтаксиса, используемый в MPS.

Поскольку при таком подходе невозможны неоднозначности, языки легко могут комбинироваться друг с другом. Вы можете расширить синтаксис языка новыми конструкциями. Вы можете вставить код на одном языке внутрь кода на другом язык, или даже вставить код на языке программирования общего назначения внутрь относительно замкнутого DSL-я. Это означает, что языки совместимы друг с другом, и это позволяет повторно использовать языки и их части, что возможно с большими проблемами в случае традиционных технологий. Мы в JetBrains много экспериментировали с такими повторными использованиями. В дистрибутив MPS входит большое количество расширений Java:
  • collections language, который позволяет более просто работать с коллекциями
  • dates language, который добавляет поддержку дат напрямую в Java
  • math language, которые позволяет писать суммы, произведения, итп математически конструкции

Большинство языков, которые мы используем для определения языков одновременно расширяют, и содержат в себе Java. Например, одна из конструкций языка для систем типов, правило вывода, выглядит как обычный DSL. Вместе с тем, внутри этого правила можно писать на Java, расширенной конструкциями, специфичными для систем типов.



Поскольку мы избавились от текстового представления, мы не можем использовать обычный текстовый редактор. Для работы с кодом мы используем специальный проекционный редактор. Для каждого узла синтаксического дерева, он создает проекцию — часть экрана с которой может взаимодействовать пользователь. При разработке MPS были приложены огромные усилия для того, чтобы такой редактор вел себя настолько близко к тестовому редактору, насколько это возможно. Например, если вы введете 1 + 2 + 3 в MPS, вы получите тоже синтаксическое дерево, которое было бы получено при разборе этой строки в Java. Конечно, проекционный редактор отличается от текстового, и существуют вещи, которые возможны в одном и невозможны в другом, и наоборот. Несмотря на это, к этим различиям можно привыкнуть, не теряя в производительности. По нашему опыту, требуется около 2-х недель чтобы стать продуктивным в проекционном редакторе.

Создание поддержки интеллектуального редактирования при работе с синтаксическим деревом напрямую сильно упрощается. Более того, во многих местах, интеллектуальные возможности предоставляются MPS IDE без каких либо усилий со стороны автора языка. Такие возможности, как автоматическое дополнение, поиск использований, переименование, работают автоматически. При разработке IntelliJ IDEA, была реализована поддержка интеллектуального редактирования для многих языков. Реализация такой поддержки потребовала больших усилий: несколько человеко месяцев на язык. С MPS аналогичные возможности могут быть реализованы в считанные дни. Это возможно, поскольку для разработки языков используются специальные языки, которые конфигурируют существующую языковую инфраструктуру. MPS это не просто редактор. Вы можете создать полноценную IDE с его помощью.

Внутри JetBrains мы используем MPS для разработки коммерческих проектов. Наша новая система учета ошибок, с кодовым именем Харизма, создана полностью на MPS, и это только начало.
Заключение

Широкому использованию DSL-ей мешают 2 проблемы: невозможность их повторного использования в системах на основе текстовых грамматик, и сложность создания интеллектуальных средств работы с ними. MPS решает обе эти проблемы путем работы с синтаксическим деревом напрямую, без промежуточного текстового представления, и предоставляя инфраструктуру для создания интеллектуальных средств работы с такими языками.

MPS 1.0 был выпущен в июле. Большая часть кода доступна под лицензией Apache 2.0 (за исключение JetBreains IDE Framework, лицензия которой позволяет использовать MPS в продуктах на основе MPS, не покупая каких бы то ни было лицензий у JetBrains).

Вы можете скачать MPS отсюда: www.jetbrains.com/mps и начать создавать языки прямо сегодня.

P.S. Мы ищем старшего разработчика в проект. Подробности в вакансии в моем профиле.
Tags:
Hubs:
Total votes 21: ↑19 and ↓2 +17
Views 8.6K
Comments Comments 69