Не создавайте собственный ЯП (DSL) для расширения функционала приложения

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


    КПДВ


    ЯП – это не основная функция приложения


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


    Что мы можем выиграть?


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

    Но давайте на чистоту.


    • "Илитарность" – это работа продажников. Продажник сделает или не сделает бренд Элитой как с новой линией производства, так и без.
    • Копирайт, как правило (не всегда, конечно), дешевле, чем разработка с нуля. А решая проблему доставки одного элемента развертыванием новой линии производства, вы только усугубляете ее.
    • Если мы хотим попробовать для себя новую сферу деятельности – не надо связывать ее с тем, в чем мы и так профи. Может показаться, что с мебелью болты толкнуть легче, но если болты не взлетят – они и мебель утащат с собой. По крайней мере, ту, которая уже стоит у клиентов.

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


    KSP – Kontakt Script Processor, или линия производства болтов в мире digital audio


    Kontakt


    Расскажу одну историю про такой язык:


    Kontakt — это ромплер (сэмплер) от австрийской компании Native Instruments. В настоящее время очень сложно найти проект с использованием виртуальных инструментов, в котором он не употребляется. За последние 10 лет Kontakt занял большую часть рынка сэмплированных инструментов. Секрет прост: в свое время, Kontakt предложил две инновации, которые перевернули подход к разработке сэмплированных виртуальных инструментов.


    Первая инновация была прямо связана с его основной функцией: он очень бережно обходился с памятью (а сэмплы в wav – это тот еще отжиратель, как HDD так и RAM). NI сделали формат lossless компрессии с быстрым декодингом и написали революционную для своего времени систему буферизации аудио.


    Второй инновацией стал KSP


    До контакта было два способа функционально организовать записанные сэмплы в инструмент, управляемый посредством MIDI:


    • Писать собственный движок с нуля на С++, или другом языке, способном использовать VST SDK от Steinberg (а ведь еще существуют и другие форматы плагинов, допустим AAX).
    • Использовать готовый сэмплер, сделанный для музыкантов, не знакомых с программированием, но имеющим звуки, которые должны быть организованы в какую-то систему. Допустим, Giga Studio. Но такие ромплеры, как правило, были либо закрытые, либо для допиливания под свои нужды требовали не меньшего штата, чем голая разработка под VST SDK.

    Контакт же угодил и тем, и этим: для быстрого прототипирования есть удобный GUI, понятный любому музыканту, прочитавшему мануал, а для дальнейшей доводки есть ни много ни мало язык программирования, с условиями, функциями (с версии 4) и стандартной библиотекой, представляющей API к большей части реализуемого через GUI функционала, а также к параметрам воспроизведения сэмплов напрямую. Кроме прочего, с версии 2 появилась возможность кастомизации интерфейса всякими свистелками и перделками, что позволило проявлять свою уникальность в почти что безграничных масштабах. А код разработчиков скрыт от глаз дважды: обфускацией и защитой от изменения инструментов.


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


    Не все так радужно


    Случилось неизбежное: инновация в виде KSP стала бичом. Пытаясь сделать синтаксис доступным для чайников, которыми являются музыканты, вместо решения реализации API на человеческом ЯП, Нативы написали собственный интерпретатор собственного языка, архитектура которого изначально не предполагала такого бурного полета фантазии разработчиков инструментов, который мы наблюдаем сейчас. Уже к версии 3 Нативы потеряли надежду угнаться за аппетитами пользователей, и просто стали клепать новые функции стандартной библиотеки, позволяя юзерам разбираться со средой разработки кода самостоятельно.


    Тем более, что уже тогда появился Nils Lieberg KScriptEditor, форкнутый со Scintilla, долгое время служивший основной IDE для KSP. Смешно сказать, но когда Нативы поняли, что контакт не справляется с размерами скармливаемых исходников, они ввели в язык функции, даже не озаботившись передачей в них аргументов. И через месяц в KScriptEditor появились taskfunc, передающие аргументы в функции, не принимающие аргументов.


    Спустя время, Нилс понял, что наступает на грабли Нативов: собственную IDE разрабатывать смысла нет. Он перенес компилятор и реализованный функционал IDE на SublimeText2, и помахал ручкой. В настоящий момент бразды правления SublimeKSP несет разработчик, кажется, из Fluffy Audio.


    В третий раз на те же грабли


    Ну вы поняли)


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


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


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


    Учитывая то, что большая часть логики препроцессора держится на макросах и inline-функциях, которые разворачивают код в огромное полотно, хранящее в себе 80% всегда истинных или всегда ложных условий (за счет подставления констант на вход условия), которые сворачиваются обратно уже на этапе разбора AST, время компиляции "правильных" исходников сопоставимо с С проектами, это в интерпретируемом-то языке для чайников.


    Сказать, что для разработчиков KSP стал болью – ничего не сказать.


    Не контактом единым.


    Я не могу привести примеры из других областей, но вот из сферы DigitalAudio:


    • Lemur – приложение для лопат с десктопным редактором, позволяющая быстро делать красивые интерфейсы для общения лопаты по протоколу OSC. Имеет свой ЯП, который можно использовать в особых script-объектах, разбросанных по всему дереву проекта. Способа сделать для него компилятор типа того, что сделан для KSP – нет.
    • ReaperDAW с развитой экосистемой разработки расширений. В итоге там, где можно, продублировала свой ЯП JSFX(ReaScript) в виде API для C++, lua и Python.
    • HISE – молодой комбайн для написания и сборки VST\VSTi, который рано или поздно убьет Kontakt от Шведского разработчика Christoph Haart. Внутри собственно редактора позволяет писать на модифицированном JavaScript, который парсится и компилируется в бинарник уже объектами C++. Идея с собственным парсером для введения дополнительных сущностей (допустим, регистровых переменных, если я правильно перевел) работала до тех пор, пока пользователи не перенесли свой код из редактора HISE в любимые IDE с подсветкой синтаксиса, статическим анализом и инструментами форматирования по JsPrettier. Сейчас Кристоф набросал пару заголовочных файлов для компиляции статических библиотек на C++, которые потом могут быть использованы как модули в редакторе. Параллельно он продолжает дополнять уже HISEScript (потому что назвать его JavaScript теперь нельзя) новыми функциями, но мы то знаем...

    Вывод


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


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


    P.S. Нет, писать свой собственный парсер для готового ЯП – тоже плохая идея.


    Буду рад любой критике к статье, первый блин и все дела.

    Поделиться публикацией

    Комментарии 55

      +4
      Поучительная история, хотя тема DSL слабо раскрыта. Как вы считатете, например, Emacs Lisp — это «собственный ЯП» емакса или переиспользование готового языка? Особенно сейчас, когда лиспа (почти) нигде нет?
        0
        Спасибо за аббревиатуру DSL. Сегодня всю ночь пытался вспомнить этот термин, и никак не мог отыскать))
        Если честно, ситуацию по emacs Lisp прокомментировать не могу, т.к. ни разу его (emacs) даже не открывал.

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

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

        вот пример одного из текущих проектов, движущегося в этом направлении
        //--------------------------------------------------------------------------
        //! g=«SampleModeling» n=«Trumpet»
        Bank 19 2 USER SMBrass/Trumpet
        //! c=legato-light i=legato o=art:100
        1 ART 1 Norm
        //! c=legato i=accented-quarter o=note-hold:36,100
        2 ART 2 Accent
        //! c=legato i=cresc-quarter o=note-hold:37,100
        3 ART 3 Fast cresc
        //! c=legato i=legato-bowed o=note-hold:38,100
        4 ART 4 PB up

        //! c=legato-dark i=legato-bowed2 o=note-hold:49,100
        5 ART 5 detashed
        //! c=legato-dark i=legato-fast o=note-hold:50,100
        6 ART 6 semi-legato

        //! c=legato-light i=legato-portamento o=note-hold:36,100/note-hold:48,100
        7 ART 7 chromatic
        //! c=legato-light i=legato o=note-hold:37,100/note-hold:48,100
        8 ART 8 mixed
        //! c=legato-light i=legato-gliss o=note-hold:38,100/note-hold:48,100
        9 ART 9 gliss1
        //! c=legato-light i=legato-portamento o=note-hold:39,100/note-hold:48,100
        10 ART 10 arabic
        //! c=legato-light i=legato-portamento o=note-hold:40,100/note-hold:48,100
        11 ART 11 pentatonic
        //! c=legato-light i=legato-gliss o=note-hold:41,100/note-hold:48,100
        12 ART 12 gliss1

        Вообще там уже потихоньку оформляется своя сокращенная if-нотация. Правда работает пока через одно место))
      +3
      Основные выводы статьи, мне кажется, сильно поспешны. По сути, имеем один пример разработки, которая изначально некачественно сделана разработчиками, безусловно компетентными в их собственной области — разработке звука — но не в разработке ЯП. Но какие выводы из этого?
      — Что скриптование вообще не нужно? Безусловно, нет — оно было нужно.
      — Что нужно было взять какой-то из стандартных языков? Очень вероятно, нет — стандартный язык с ходу тянет за собой толстую экосистему, привинтить которую надо ещё суметь, а затем обновлять её реализацию, зависеть от развития чужого проекта…
      — Что надо было сразу покупать услуги толкового специалиста? Возможно, но если они не представляли себе вначале, куда и как развиваться и даст ли эта попытка какую-то практическую пользу, то не было причин считать, что такое привлечение окупится.

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

      Не надо бояться изменений. Надо бояться, наоборот, устареть.
        +7
        Взять один из стандартных языков — не такая уж и проблема. Lua, например, весьма легковесный и несложно биндится к приложению простыми вызовами С-функций. Хочется JavaScript — берём V8 или более простой Duktape, который можно хоть на микроконтроллере выполнять.
          0
          > Взять один из стандартных языков — не такая уж и проблема.

          Технологически. Но не по массе других факторов. Как интегрируется с основным приложением? (Не «как передать int в JS», а какие структуры строить, как их соединять, какие параметры давать пользователю, а какие вычислять самому, и т.д.) Как административно обеспечивать сопровождение движка, включая жалобы своих пользователей, импортировать обновления стоковой версии… и ещё масса проблем.

          > Lua
          > JavaScript

          Вот именно, что я принципиально не хотел опошлять обсуждение до подобных деталей, по причинам см. выше в этом комментарии.
            +2
            В том-то и дело, что технологически парсер под свой DSL написать может и не так сложно. Ну месяц может на что-то сложное уйдет. По сути мы что в первом случае, что во втором имеем проблему трансляции исходного кода на упрощенном, или, скажем, более удобном языке во внутреннее представление.
            Не имеет большого значения, является ли этот исходный код DSL, sql или JavaScript.

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

            Но с другой стороны, кто его знает, может DSL и правда пишет больно компетентный человек, который уже собаку на такой разработке съел)).
              +1
              Правильный DSL — это не свой язык. Это имеющийся язык, которые имеет достаточно гибкости, чтобы делать на нем DSL. Такими языками, из хорошо лично знакомых, мне в мире JVM являются Scala, groovy, теперь еще и Kotlin, а в мире Windows — jscript и vbscript (под общим названием wshell). Посмотрите хоть на gradle для примера — это DSL на базе groovy, для описания сборки проектов.

              И поверьте — такой задачи, как выдать вам AST вообще обычно не стоит, по той простой причине, что для создания DSL это не нужно.
                +1
                Согласен, об этом и хотел сказать (интересно, где не получилось).
                Ведь, в принципе, если в качестве DSL используется уже ЯП общего назначения, это скорее не DSL, а API. Разница, мне кажется, в том, что DSL – замкнутая система, а API находится в некотором домене, который диктует способы его вызова.
                И согласен, что с AST меня занесло чутка: просто моя область интересов очень критична к рантайму (да и к памяти тоже, как правило), да и вообще основная буча происходит в лисенере 48000FPS)) Поэтому я несколько однобоко воспринял концепцию «внешнего языка» как, преимущественно, препроцессора.
                  0
                  >DSL, а API
                  Ну, это зависит от того, какой язык выбрать. Хотя гибкость в рамках одного языка конечно ограничена, но скажем что-то типа xml или sql внутри ЯП — вполне достижимо. Из практики — jscript, vbscript — в чистом виде API, эти языки не позволяют делать настоящий DSL. Но на groovy или скале уже пишется вполне нормальный DSL, синтаксис которого на первый взгляд не очень и похож на исходный.
                  0
                  То, о чём вы говорите — это eDSL.
                    0
                    Ну да, наверное, и что? Какая, по большому счету, разница в данном конкретном случае (для условной музыкальной программы)?

                    Я и хочу в общем сказать, что изобретать свой собственный язык — не слишком хорошая идея, язык который хостится в другом языке, готовом — это как правило проще и удобнее с большинства точек зрения.
                      0

                      Плохая идея — делать то, в чём ты не разбираешься и не хочешь разобраться. А свой язык ничем не хуже своего класса и своей функции. Особенно смешно читать про JS. У eDSL есть не меньшее число неудобств. Навскидку:


                      1. Необходимость всем знать много более сложный хостовой язык.
                      2. Тысячи способов выстрелить себе в ногу самыми разными способами.
                      3. Тормоза от интеркоммуникации между двумя разными рантаймами.
                      4. Дикое потребление памяти виртуальными машинами.
                      5. Сложности со многопоточной обработкой, интегрированной в общий пайплайн.

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

                        0
                        >Необходимость всем знать много более сложный хостовой язык.
                        Возможность применить, если вдруг стало нужно, намного более мощный язык — это достоинство, вообще-то. А необходимость его знать — это как бы требует подтверждения. Посмотрите на тот же kojo, и расскажите моей дочке, что ей нужно знать скалу, чтобы нарисовать квадратик при помощи Logo. Она и слова-то такого не знает, что характерно, для нее это лого.

                        Ну или gradle, или jenkins — его язык пайплайнов это eDSL на базе groovy. Я каждый день вижу, как люди на этом пишут, совершенно не вникая в то, что это groovy. Полезно знать? Да. Необходимо? В общем случае нет.

                        Так и с остальными пунктами, кстати. Я вполне верю, что ваш опыт отличается от моего, но в моей практике пункты 2-5 либо вообще места не имеют, либо их влияние сильно зависит от дизайна — то есть, оно под нашим контролем.
                          0
                          Ну а теперь представьте, что кто-то воспользовался какой-либо фичей из хостового языка. Много она поймёт без знания этого языка?

                          Было дело ковырялся я в билд-конфигах на груви. Больше не хочу.
                            0
                            Не вижу в этом никаких проблем. Язык описан, знающие люди есть.

                            Но если у вас доступа к другому языку нет — то как только возникает потребность, уже это становится настолько большой проблемой… и тогда у нее вряд ли вообще есть простое решение.

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

                              +1
                              Разобраться в ДСЛ, зная предметную область, легко может любой, знающий предметную область. А вот чтобы разобраться в коде на языке общего назначения, нужно быть программистом, которому ещё и не лень изучать этот язык, если он его ещё не знает.

                              Ну а то, что для ява-прогеров, груви после XML- это откровение, не удивительно.
                                0
                                Мне тут коллега риторический вопрос похожий задал:
                                Сколько вменяемых скриптеров под контакт ты знаешь? Каждый 5-ый музыкант или каждый 10005ый?

                                продублирую и сюда:

                                Всего интересующихся и пишущих, пожалуй, несколько сотен. Код которых видел лично – человек 20, вменяемый из них – один. И то, потому что он именно скриптер KSP до мозна костей. Общаться с ним как с программистом больновато. Вместо языка, в котором разберется любой, знающий предметную область, получился язык, на котором может хорошо писать только человек, который до глубины души выучил этот язык.
                                Один раз я еще видел очень хороший код, в библиотеке CinePianos (пианины), но не знаю, кто автор.

                                Вполне могу предположить, что это слабое свидетельство, основанное на личном опыте и конкретном кейсе.
                            +1
                            Ну или gradle

                            Справедливости ради, maven, не смотря на xml, гораздо понятнее и проще для восприятия. Все потому, что у него есть четкая понятная структура. Это примерно как выбор из меню, против команд консоли.
                              0
                              Тут gradle — просто как пример успешного DSL на базе груви, а не для того, чтобы за него агитировать :)
                                0
                                Ну вот я и сомневаюсь в «успешности» DSL. Конечно, за счет DSL gradle мощнее того же мавена, но порог вхождения также гораздо выше. С нуля, не подглядывая, написать конфигурацию maven в разы проще. Вообще, это проблема всех DSL — синтаксис очевиден только авторам, остальным нужно постоянно смотреть на документацию. Есть, конечно, исключения типа SQL, но такого немного.
                                  0
                                  >синтаксис очевиден только авторам
                                  Хм. Вообще-то, groovy DSL для camel, к примеру, как раз хвалили за то, что autocompletion работает на ура, и всегда понятно, что именно можно ввести в конкретном месте.
                              0
                              > Возможность применить, если вдруг стало нужно, намного более мощный язык — это достоинство, вообще-то.

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

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

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

                                То есть, я когда говорю «лучше» — это в данном контексте нужно понимать как «проще в реализации», в первую очередь. То что у этого варианта есть свои недостатки — я и не спорю в общем-то.

                                >Чем меньше ему требуется выходить за пределы того, что он знает и как думает, тем лучше для общего результата.
                                Вы мне поясните, а зачем ему выходить? Вот мне часто приходилось слышать, что скала например сложный язык. И я по большей части с этим совершенно согласен. Но я также знаю, что в своей работе я ежедневно использую Spark Shell, который по сути внутри является Scala REPL. И мне почему-то совершенно не требуется выходить за пределы небольшого подмножества языка, где даже if или циклов по большому счету нет.

                                Тоже самое можно вполне применить к jenkins pipeline. Вы вполне можете писать пайплайны в декларативном стиле, вообще не понимая, что это groovy:

                                pipeline {
                                    agent any 
                                    stages {
                                        stage('Build') { 
                                            steps {
                                                // 
                                            }
                                        }
                                        stage('Test') { 
                                            steps {
                                                // 
                                            }
                                        }
                                        stage('Deploy') { 
                                            steps {
                                                // 
                                            }
                                        }
                                    }
                                }
                                  0
                                  > Вы мне поясните, а зачем ему выходить?

                                  Ситуация: Петя написал скрипт. Петя профессиональный программист, скрипт соответствующе написан с залезанием в хитрые особенности, которые ему давно привычны.
                                  Вася пытается пристроить этот скрипт себе, модифицировав по обстановке. Вася музыкант, понял что такое цикл, но вспоминать, например, тонкости применения this в JavaScript — у него мозг свернётся в трубочку.
                                  Если в языке просто нет средств, которые можно поабьюзить далеко за пределами цели применения — Вася не будет выходить дальше того, что ему и понятно, и нужно. Если же они будут, то он с хорошей вероятностью откажется от использования скриптового средства просто потому, что он не в состоянии контролировать его опасность.

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

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

                                  Мне это тоже психологически диковато, но я знаю о проблеме, на основании некоторого опыта общения с такими. И это включало даже старых программистов, которые успешно писали на чём-то типа Фортрана, но их жестоко вымораживал C. На родной кафедре таких одно время была чуть ли не половина.
                                    0
                                    Мне кажется, мы пошли по второму кругу :)

                                    Я понимаю, что такие языки, у которых ЦА не программисты, они сильно специфические. И что для таких лучше, я сказать не могу, и не на каком варианте не настаиваю.
                        0
                        > Правильный DSL — это не свой язык. Это имеющийся язык, которые имеет достаточно гибкости, чтобы делать на нем DSL.

                        Категорически — нет.
                        Это вы как раз получите универсальный язык с заточкой (скорее всего не в языке, а в библиотеке) под целевую область.
                        DSL же характерен не тем, что он может (и насколько он может выполнять эквивалент машины Тьюринга), а тем, что он не даёт сделать, в чём ограничивает (или хотя бы явно обозначает красными флажками). Именно потому, что он domain и specific.

                        Java, C#, Python и другие языки с AMM — DSL по сравнению с языками более низкого уровня с ручным управлением памятью (C/C++, Pascal и т.п.) Доменом является автоматическое управление памятью, ограничением — запрет на ручное управление.

                        Аналогично, пусть без автоматического управления, но с минимизацией игр с указателями, Fortran — DSL для записи математических расчётов.

                        Язык команд x86 является DSL по сравнению с языком микрокоманд конкретного Core Duo или Sandy Bridge. Доменом является переносимая система команд x86.

                        Декларативный язык является DSL по сравнению с процедурным языком. Доменом является поддержка описания зависимостей, условий и требований вместо ручного вычисления метода исполнения. Это в равной мере справедливо, например, для SQL и для Makefile. Можно то же сказать про HTML, хотя повальность JS портит картину.

                        Введение DSL, он же язык более высокого уровня, вызывается конкретным преимуществом его введения.

                        Типичными преимуществами являются:
                        * Независимость от особенностей реализации (как переносимость системы команд, написания на C или доступные индексы для СУБД).
                        * Устранение «геморроя», сиречь неприятных накладных расходов на реализацию. Характерный пример — ручное управление памятью.
                        * Необходимость вывести определённые сущности из ментального поля автора реализации. Уже работа с указателями доступна не всем программистам, а учитывать сочетаемость акций в АЛУ конкретной модели сможет десяток человек по всему миру.

                        И вот вопрос, как именно сделать, чтобы достичь преимущества для конкретной цели — и требует совместной работы высокоуровневых специалистов как в целевой области, так и в языках (чтобы не попасть на детские болезни). Решение методом внедрения широкораспространённого языка, хоть и с целевой библиотекой — решение тупое и далеко не всегда лучшее. Где-то вообще неуместен процедурный язык, а нужен декларативный, а где-то не подойдут конкретные фишки типа «в JS любое число — double», но специалист по языкам нужен хотя бы для того, чтобы знать, о чём спрашивать в плане поиска граблей.

                        Приводившийся здесь пример с FAR ужасен потому, что язык плагинов FAR — язык для программистов. DSL для расширения для не-программистов, пусть даже с базовыми свойствами языка программирования типа последовательностей действий, циклов, функций, и т.п. — должен быть максимально приближен к тому, как думает целевая группа, и избавлен от всех граблей пусть даже всемирно популярных языков. Поэтому всякие JS должны рассматриваться одними из последних (список его граблей не известен только тому, кто ленивее Емели с печи).
                        Чем ближе DSL к тому, как излагал бы свои задачи специалист целевой области, тем лучше. (Естественно, с поправкой на формализацию синтаксиса и выкидывание нецелевой лексики, где она используется.) Идеала никогда не будет, но надо стараться приблизиться к нему.
                          +1
                          Вы немного не так поняли, отсюда и все эти претензии по большей части. Речь не о том, что условный groovy это DSL. Вовсе нет, groovy это не DSL, это язык, на котором удобно делать DSL.

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

                          >Приводившийся здесь пример с FAR ужасен потому, что язык плагинов FAR — язык для программистов.
                          С каких это пор программистам не нужен DSL? Они тоже вполне себе ЦА для таких языков, и им он тоже необходим — причем как можно более полноценный. Поэтому и Lua.

                          >Категорически — нет.
                          Ну, я вас услышал, но меня вы не убедили. Возможно это решение не всегда лучшее — но оно практичное. Это работает, и это проверено многократно. И совершенно понятно, что сделать такой язык намного проще, чем спроектировать с нуля свой, и при этом не забыть ни одной фичи, которая будет впоследствии нужна кому-то.
                            +1
                            > С каких это пор программистам не нужен DSL?

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

                            > Вовсе нет, groovy это не DSL, это язык, на котором удобно делать DSL.

                            Повторюсь, проблема в том, что возможность выйти за рамки DSL, ограниченном в стиле того, как вы описали в примере с pipeline, даёт возможность, которая будет широко использоваться и отторгнет этим самым часть пользователей.
                              0
                              >отторгнет этим самым часть пользователей.
                              Может. Но мне кажется, собственный (как правило, ограниченный и достаточно примитивный) язык, который не позволяет делать что-то нужное, отторгнет еще быстрее.

                              Сможете вспомнить успешные примеры, когда DSL сделан как собственный язык, с нуля, а не как eDSL? Мне вот что-то такие вообще в голову не приходят — вспоминается либо groovy, либо Pyhton, либо Lua, либо Lisp в том или ином виде.
                    +1
                    Ну вот например у FAR внутри есть Lua. Что-то я не вижу всех этих проблем, что вы нам рассказываете. А у множества приложений в мире JVM есть свои DSL в виде groovy, и тоже никаких проблем нет. И JavaScript в том же мире JVM есть — раньше в виде Rhino, теперь Nashorn, и многие его используют как DSL.

                    > какие структуры строить, как их соединять, какие параметры давать пользователю
                    Это нормальная программистская задача любого разработчика DSL. Она уже была успешно решена десятки, если не сотни раз.
                      0
                      > Ну вот например у FAR внутри есть Lua. Что-то я не вижу всех этих проблем, что вы нам рассказываете.

                      Автор FAR специалист во множестве суботраслей программирования.
                      Но ожидать такого же уровня от авторов произвольной узкоцелевой программы как-то сложно.

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

                      Успешно — наверно, десятки тысяч. И провально — сравнимо по количеству.
                        +2
                        >Но ожидать такого же уровня от авторов произвольной узкоцелевой программы как-то сложно

                        Ну, наверное да. Это разные специализации и это нормально.

                        У меня как-то на хабре возникала дискуссия на тему образования, нужно ли всех учить основам разработки компиляторов — грамматики там, AST, и т.п. темы. И мы не пришли к общему мнению. Для меня (из практики) это вполне рядовые задачки, хотя и не могу себя считать специалистом в области компиляторов. Но видел и мнение людей, которым это никогда не требовалось.

                        При этом для встраивания в свой проект DSL на условном groovy вообще не нужно быть специалистом по компиляторам — это совсем другая задача, она проще на порядки. Я только в своих проектах это делал наверное раз 5, не меньше.

                        >Успешно — наверно, десятки тысяч. И провально — сравнимо по количеству.
                        Если есть десятки тысяч успешных проектов, то по крайней мере всегда можно либо подсмотреть, как там сделано, либо позвать кого-то на помощь. Главное, что это не какой-то супер-пупер проект, который никто не знает, как делать.
                          0
                          > Автор FAR специалист во множестве суботраслей программирования.

                          LUA & SQLite добавляли энтузиасты уже после перехода FAR в open source.
                            0
                            Спасибо, упустил это. Надеюсь, у них тоже неплохо получилось.
                      0
                      Кстати, когда-то соперник того же Kontakt MatchFive тот же lua и использовал для скриптования. Сейчас проект получил развитие, все живы.
                      Согласен, более чистые способы есть, см. коммент выше
                    0
                    del
                      +5
                      Статья на самом деле ни о чем, скорее о боли автора.
                      DSL (domain specific language) — это значит что такой язык, который приспособлен к предметной области.
                      В частности например ноты — это DSL для музыкантов, а например конструкторская документация со всякими ГОСТами — это DSL для конструкторов.
                      С помощью таких DSL осуществляется передача информации между разработчиками, компьютер тут это несущественная сторона, которая проинтерпретирует DSL согласно правилам, и которому все равно какой поток байтов кушать. DSL создаются людьми для людей, и это самый главный факт.
                      То что в данном случае разработчики сделали такой DSL какой они сделали, говорит лишь об их некомпетентности как разработчиков-DSL, и ничего больше.
                      Если они пытались сделать язык общего назначения, то конечно лучше бы взяли готовый.
                      Ну а если бы они пытались сделать действительно DSL для музыкантов, то скорее всего они провалились, а язык общего назначения не помог бы в этой области.
                        0
                        Трудно с вами не согласиться :)

                        Поэтому просто прокомментирую последнюю строчку, ответ на которую, в основном и предполагался статьей. Видимо, был чересчур категоричен)
                        Ну а если бы они пытались сделать действительно DSL для музыкантов, то скорее всего они провалились, а язык общего назначения не помог бы в этой области.

                        Вот небольшой разговор с музыкантом
                        (на самом деле, не имеет значения: музыкант, дизайнер, бухгалтер)
                        ну да, весь вопрос только в том, чтобы это так наз. «программирование» было доступно даже чайникам… Мне вот до сих пор не понятно как программируется вроде бы имеющий «дружеское» гуи логический редактор в кубе… ) Хоть и нужен он раз в год, но без талмуда там ни в зуб ногой ничего не понятно… Хотя пару раз я и за талмуд брался, но безрезультатно… А вот кубовский «программатор» экспрешн мэп интуитивно понятен и без талмудов, и любой чайник в нём может разобраться за полчаса.

                        Вы от хоста хотите столько наворотов, подвластных ЛИЧНО ВАМ :) Логично предположить, что вы сможете ему изъяснить свои желания на языке булевой алгебры. А то меня уже тут неделю брутфорсит человек, который хочет писать нотками миди во фруктах и получать на выходе «неотличимо от живаго». ;)
                        А разницы между текстовым интерфейсом и ГУИ принципиальной нет, как ни странно) Текстовые конфиги, тем более, всегда можно лицезреть в одной удобоворимой форме в любимом текстовом редакторе (vim там, или sublime text), а в гуи пока нужную кнопку найдешь....

                        что прям на русском языке можно писать? :) «Дорогой Кубэйс зпт сделай пжлст на пятом канале перевод 121-го контроллёра в програм чендьжъ номер 10!» :D
                        Или на каком? :cool:
                        У меня на англ. то времени нет, а ты хочешь чтобы я ещё и языком программирования занимался. Не серьёзно (для активно практикующих аранжировщиков :)) Нужно — открыл, на кнопочку назначил, работаем дальше. На «писанину» ох как много времени уходит…

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


                        Зуб даю, не глупый человек, профессионал с большой П, мне до него в некоторых вещах расти и расти. Только… Может ну их, музыкантов? Мне кажется, если б единственным вариантом облегчить себе жизнь было – писать динамическую библиотеку на C – я б все равно этим занимался, может просто чутка дольше))
                          0
                          Увидел название статьи — прям всё про меня.
                          Думается проблема в том, что на начальном этапе — свой ПРОСТОЙ язык — это хорошо, а когда он разрастается во что-то большое и при этом нестандартное -это уже ПЛОХО. Мы в лаборатории написали свой микроязык (да — DSL) для управления одновременно аналого-цифровым преобразователем и координатным устройством (с парсингом, и т.п...). Там команд с гулькин нос и они все умещаются на половине листа А4 крупным шрифтом. И (ура!) это творение не надо развивать. Оно вполне самодостаточно. Там задействовано одно АЦП и один конкретный координатник (что-то типа станка с ЧПУ). Стандартные ЯП включают в себя некие свойства обобщения — которые в нашем случае излишни.
                        0
                        у меня другая история с языком программирования, положительный опыт. StarTrinity CallXML. Есть скриптовый язык, есть для него и обертка GUI. вы зря такое категоричное название для статьи придумали.
                          0
                          Отличная кейс из мира музыки. Кто-то должен был рассказать про это, но лично мне не хотелось :)

                          Вообще мир музыки — непочатый край странных кейсов программирования. Там, покопавшись, можно ещё и не такое найти (один движок FL Studio, написанный на Delphi чего стоит).
                          Кстати, была ещё такая штука — NI Reaktor, которая позволяла клепать свои инструменты вообще с нуля на (если мне не изменяет память) графическом ЯП. Что с ним стало сейчас, правда, не слежу. Но уже тогда эта фиговина казалась абсолютным оверкиллом, в котором на практике что-то собрать было довольно трудно.
                            +1
                            Технология Core в NI Reaktor это как раз пример вполне профессионального подхода к созданию DSL. Внутри это потоковый DSP-язык с JIT-компилятором. Тут уже другой момент — высокий порог вхождения. Но для музыкантов это не должно быть чем-то новым. Вспомним высокий порог вхождения в скрипку, ф-но или даже в минимуг.

                            Но вопрос интересный. Какой язык предоставить музыканту, знакомому с модульным синтезом, но не с программированием? У меня подозрение, что граф. программирование в духе Reaktor или Max/MSP здесь будет предпочтительнее какого-нибудь JavaScript.
                              –1
                              Дискуссионно. Я не могу принимать участие в дискуссии, ибо как являюсь заинтересованной стороной — пишу на C#. Поэтому мой ответ известен заранее — ДАЁШЬ C#!!! Но кмон, будем объективными, мало кто это разделяет.
                                0
                                Ну, насколько я понимаю (сам в реакторе так ничего и не делал), это нечто вроде САПР для электронщиков. Просто САПР модулирует сигнал и циферки показывает, а реактор на выход звук выдает. Так что этот гуй вполне обоснован, ничего сверхъестественного там не должно быть)
                              +3
                              К сожалению, по тематике DSL существует не так много источников на русском языке. Но в мировом масштабе о DSL существует целый ряд монографий. На что важно обратить внимание: использование металингвистической абстракции является эффективным способом борьбы со сложностью программных проектов, а встроенный в приложение язык — это эффективный способ обеспечения расширяемости приложения.

                              Можно согласиться с следующим положением: невежество, дилетантизм в области простроения языков наносит вред проекту. В качестве встраиваемого языка лучше сначала рассмотреть готовое решение: Lua, Scheme, Python и так далее.

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

                              При этом определенные предметные области вполне заслуживают создания DSL. Особенно, если нотация получается декларативной, не тьюринг-полной. Раз уж исходно был взят музыкальный контекст, то здесь можно вспомнить язык LilyPond. Он, с одной стороны, является декларативным («с4 e g e c»), а с другой — расширяется с помощью Scheme.

                              Здесь любопытно, что та же Lua — это как раз пример DSL (миниязык для встроенных применений), который кто-то за нас уже написал. В своей работе, вы, возможно, используете и иные «чужие DSL»: make, TeX, graphviz и так далее.

                              Кроме того, собственный DSL может быть разумным решением, если того требуют определенные условия функционирования встроенного языка. В своей заметке автор пишет по поводу Reaper: «в итоге там, где можно, продублировала свой ЯП JSFX(ReaScript) в виде API для C++, lua и Python». Тут явная путаница. В Reaper используется тот же Python для задач автоматизации редактирования на уровне piano roll. А вот собственный DSL JSFX, который напоминает Си, был создан для задач обработки аудио и MIDI в реальном времени. Язык, к сожалению, тоже отдает дилетантизмом, но, с другой стороны, а где было взять готовый небольшой встраиваемый DSP-язык для работы в реальном времени?

                              Подводя итог. Разработка DSL — задача, которая требует определенного опыта и знаний. Причем, подобные знания, во многом, за пределами типичных университетских курсов по теории компиляторов.
                                +4

                                Заголовок:


                                Не создавайте собственный ЯП (DSL) для расширения функционала приложения

                                Первый абзац:


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

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


                                Проще говоря, DSL — это инструмент, у которого есть набор полезных применений, и его, как и любой инструмент, надо правильно применять.

                                  0
                                  А можно для общего развития пример расширения функциональности (сторонним пользователем), не являющимся плагином?
                                  Просто мне архитектурно кажется, если есть такая возможность, то, скорее всего, есть некая точка подключения, которая грузит сторонние модули (или плагины) и работает с ними. Тот же SublimeText или vim.
                                  Вот в браузерах я так и не понял: есть плагины, а есть расширения, чем они конструктивно (или идеологически) отличаются?
                                    0
                                    Тот же Forth (Форт) язык может казаться странным не знакомым с ним, но обладает свойством полной расширяемости приложения во время его исполнения, если ядро Форт-системы специально не закрыто для доступа.
                                    С помощью этого язык расширяют необходимой функциональностью создавая DSL вариант средствами доступными в базисе языка.
                                    P.S. Тот же HSML, приведённый выше, построен в качестве расширения исходного Форт языка и скорее всего подгружается как расширение, но бывают и другие схемы интеграции Форт реализованных в разных языковых базисах.
                                    Расширение Форт языка может превратить его в синтаксически/семантически другой язык.
                                      +1
                                      А можно для общего развития пример расширения функциональности (сторонним пользователем), не являющимся плагином?

                                      Самый простой пример — это макросы в Excel. Ну и вообще любая внутренняя автоматизация в системах, которые дают такую возможность.

                                        0
                                        В браузерах — уровнем, на котором могут воздействовать. В Firefox, plugin — это сущность уровня C-интерфейса (и подключается как so/dll), а addon — JS. Различается и набор того, что они могут сами делать и как получать данные, но это уже надо глубоко доку пересказывать.
                                      0
                                      Есть такая замечательная платформа 1С. В не реализован встроенный язык. 1С декларирует основное назначение встроенного языка — описание отличий поведения платформы от типового. Сам язык очень простой с одной парадигмой — процедурной. Вся мощь и функциональность языка реализуется через многочисленные встроенные объекты: работа с данными, работа с интерфейсом, работа с файлами, реализация внешних интерфейсов: http, soap, odata.
                                      На встроенном языке 1С пишут разнообразные приложения и не только для решения бизнес-задач. Разнообразие встроенных объектов платформы позволяет писать на 1С наряду с бухгалтерией, торговлей и зупом такие приложения как шина данных. И вот уже на встроенном языке написаны конфигурации с объемом кода, сопоставимом с таковым, если бы теже задачи решались на языке общего назначения. На встроенном языке уже начинают решать задачи, которые выходят за рамки DSL, изначально декларованного для платформы.
                                      Все это приводит к тому, что уже возникает потребность в развитии встроенного языка, т.к. его изначальная простота не позволяет строить сложные надежные приложения. И здесь мне видятся два пути развития:
                                      1. всю функциональность, потребность в которой выражена в виде реализованного функционала на встроенном языке, нужно переносить во встроенные объекты платформы и т.о. возвращать первоначально назначение встроенного языка;
                                      2. развивать встроенный язык до уровня языка общего назначения.
                                      Последний вариант, как и показано в статье, ведет в никуда. В этом варианте фактически DSL перестает быть таковым и теряется главное его преимущество — не решать алгоритмические задачи, а решать задачи предметной области.
                                        0
                                        Для скриптов игрового бота, который работает в «реальном времени» (с оговоркой, что Windows не ОС реального времени) мы взяли Паскаль, добавив туда предопределенные функции, специфичные для бота. Выбор обусловлен простотой (сегодня многие считают его примитивным) и широкой известностью Паскаля, кто не знает — достаточно прочесть статью в Вики, можно 30 стр описания Вирта — какой ЯП имеет меньшее описание? ;) При этом: это универсальный ЯП, где в отличае от многих скриптовых языков не надо ломать голову, как решить простейшую задачу, о которой авторы не подумали (вспомнить, нпр., командные файлы первых версий MS DOS).
                                          0
                                          Сразу вспоминается Origin с его кошмарным скриптовым языком LabTalk.

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

                                          Самое читаемое