ЯВД: Куда и как идти?

Направление движения для меня определено — Интенсивное программирование и так же определена промежуточная точка — семейство языков «Языки выходного дня».

Разработка такого уровня экспериментальности может быть только итеративной — сделали шаг, проверили, вернулись, переделали. Поэтому неверно пытаться сначала полностью разработать ЯВД, а потом идти дальше. Надо как можно быстрее дойти до шага «проверить».

Что для этого нужно? Хотя бы один язык, пусть очень черновой и минимальная экосистема: компилятор, система выполнения, библиотеки.

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

Итак экосистема: язык + компилятор + среда исполнения + минимальный набор библиотек.

Начну с языка. Как я уже писал, семейство предположительно состоит из

  • языка уровня 1: динамически типизированный язык (желательно с возможностями gradual typing);
  • языка уровня 2: статически типизированный язык с динамическими возможностями и сборкой мусора;
  • язык уровня 3: статически типизированный язык с ARC;
  • язык уровня 4: статически типизированный язык с ручным управлением памятью.

Эта классификация уже показала свою применимость. Кратко мы говорим, например, L2 (язык уровня 2), и это сразу дает понимание.

Другой взгляд на те же уровни через область применения:

  • L1: приложения
  • L2: приложения и высокоуровневые библиотеки;
  • L3: приложения, критические по производительности или безопасности, и библиотеки;
  • L4: ОС, драйверы, низкоуровневые библиотеки.

Понятно, что классификация условная,  особенно для языков-монстров типа С++. Кроме того, 4-х уровней явно недостаточно, поэтому надо использовать опыт нумерации строк в Бейсике :) и добавить пару нолей, то есть добавить подуровни.

Если попытаться разбросать существующие языки (не сильно задумавшись и очень примерно):

  • Typescript — L100
  • Go — L220
  • Swift — L300
  • Rust — L400
  • C++ — L420
  • C — L440
  • LLVM IR — L470

С какого языка начать? Мой опыт говорит о том, что для проверки языка принципиально важно написать на языке компилятор самого языка (bootstrapping).

Значит, язык должен быть

  1. маленький и простой с точки зрения компиляции;
  2. достаточный для того, чтобы на нем написать компилятор (в том числе в последствии и для других языков семейства);
  3. достаточный для экспериментов с компонентами — впрочем, это не сразу.

На мой взгляд, надо начинать с языка L3. Отсутствие GC упрощает экосистему (хотя потребует weak pointers или чего-то аналогичного).

И тогда последовательность шагов примерно такая.

Этап 1:

  1. минимальная грамматика языка
  2. парсер
  3. минимальный семантический анализ
  4. кодо-генератор. Думаю, что строить надо код на С, что достаточно просто и еще упрощает взаимодействие с C библиотеками.
  5. система выполнения на С/С++
  6. минимальный набор библиотек (в начале обертки для C libraries)

Этап 2:

  1. bootstrap
  2. система тестирования
  3. улучшения

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

И последний вопрос — на каком языке писать первый компилятор?

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

22 комментария


  1. Выглядит так, что под итерациями Вы понимаете нечто вроде такого?

    ПОВТОР
    1. Формулируем/корректируем языки
    2. Пишем/меняем инструментарий
    3. Используем языки
    4. Оцениваем
    ПОКА не удовлетворит оценка

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

    Ответить

    1. Да, итерации примерно такие.

      Языки уровня L2 проработаны очень подробно. Проработано даже два разных языка, которые сильно отличались набором дополнительных требований.

      Естественно, что ЯВД будет делаться на опыте, но даже L2 в ЯВД будет существенно отличаться, хотя бы тем, что нет тех доп. требований нет и есть требование «семейственности».

      Еще одна причина того, что я хочу начать с L3 — это чтобы посмотреть на задачу с другой стороны.

      И еще есть одно обстоятельство, которое должно ускорить работу — изначальный L3 будет содержать только необходимые конструкции. Расширение только после переписывания компилятора на себе самом, а это значит, что расширения/улучшения будут более обоснованными.

      Ответить

      1. Вот семейственность и выглядит как нечто, требующей одновременного существования языков, чтобы попробовать семейственность на деле, и скорректировать соответствующим образом проекты языков. Но если исходить из последовательной разработки языков вместе с инструментарием, столкновения можно ждать очень долго, а если не повезёт, то и слишком поздно.

        Ответить

  2. выкинули два класса языков:
    — метаязыки, такие как MPS и XText (в примитивном случае сжимаются до lex/yacc), позволяющие напрямую формулировать и синтаксис, и семантику
    — Форт который похоже невозможно переплюнуть по простоте и базовости, даже ассемблер сложнее по грамматике (у Форта её по факту практически нет, парсер состоит только из примитивного лексера в десяток-два строк кода)

    Ответить

    1. Не понял. Я вообще ничего не выкидывал. Я строю главную последовательность, вокруг неё могут существовать другие последовательности, например, что-то типа Форт.

      Форт чудесный язык (то есть находится за гранью нормальности). С другого края шкалы нормальности, расположен другой чудесный язык — С++.
      Опыт у меня есть и на том и на другом, впрочем, как и на многих других языках разной нормальности. Но использовать в работе чудесные языки я не хочу, это слишком трудозатратно (и скучно).

      Ответить

  3. Я бы пошёл по пути Мульти-Оберона: по факту, база языка одна и та же. Декларация возможностей в начале объявления программы специальной директивой, которая добавляет новые функциональные возможности.
    Отнесение Go ко второму уровню откровенно ошибочно — при желании можно написать ОС. Сборщик мусора можно отключить, ассемблер компилятором поддерживается.
    Имхо, язык перекрывающий все 4 уровня должен начинаться с самого верхнего уровня — его можно реализовать вообще на чём угодно. Он максимально оторван от железа. С него перенос на другие платформы и должен начинаться.

    Ответить

    1. если честно, вообще не понимаю зачем такие развесистые иерархии языков, когда принцип универсального языка программирования давно известен:
      — метаязык для описания грамматики и семантики DSL-языков,
      — и программирования через кодогенерацию (вывод кода в файлы)

      т.е. должны быть

      1) средства BNF-подобного описания грамматики (или может быть DCGб так как этот класс грамматик позволяет реализовать произвольные контекстно-зависимые языки)

      2) структура данных, заточенная на работу с AST: если ядро на Python пишется, в их качестве прекрасно работают объектные графы

      3) язык с операциями, заточенными на преобразования графов, и форматный вывод строковых деревьев в файлы (скрипты для сборки, исходных код на произвольных языках)

      в п.3 как минимум должно выключаться полноценное сопоставление с образом (унификация), см. Elixir, Rust, Prolog

      Ответить

    2. «язык перекрывающий все 4 уровня» — это как раз совсем НЕ ТО, что я делаю. ЯВД — это не универсальный язык, а семейство языков.

      «Отнесение Go ко второму уровню откровенно ошибочно» — как я уже писал, классификация условная. Хотя Go минус GC — это совсем не Го, так как очень многое завязано именно на GC и на escape analysis.

      По поводу с чего начинать разработку языка — я считаю, что со средины, то есть L2 или L3. То есть с того уровня, на котором я предпочитаю писать компилятор.

      Ответить

  4. Запоздалый комментарий «по форме» — может, стоило уровни нумеровать не с верхнего уровня, а с нижнего, от машкодов и ассемблера? А то ведь сверху-то пределов нет :) А у вас там уже 0 близко :)

    Ответить

    1. До нуля от уровня 100 еще 99 пунктов, а не хватит умножим еще на 10 :)
      Если серьезно, то за пару лет использования я привык к этой классификации. Она удобна.

      Ответить

  5. Алексей, приходите в ЯОС, уже есть инфраструктура, вся маленькая, управляемая, кроссплатформенная, плюс-минус развитая. Обойдём C++ стороной. Сборка мусора есть, можно её ворочать куда хотите. В Обероне Вы уже как рыба в воде. Запускается на интересной плате Zybo с ПЛИС — есть простор для развития (тем более Алексей Морозов опубликовал старую версию Active Cells даже с инструкцией). Красиво, изящно, хоббийно.

    Ответить

    1. Если «приходите» это делайте исполнение Тривиля на ЯОС, то это слишком узко. Мне надо везде :) А начать движение к «везде» проще с той платформы, на которой делаешь компилятор.

      Ответить

  6. Вашу классификацию я бы слегка модернизировал, т.к. мир развивается, акценты смещаются.

    У1: приложения
    У2: приложения и высокоуровневые библиотеки;
    У3: приложения, критические по производительности или безопасности, и библиотеки;
    У4: ОС, драйверы, низкоуровневые библиотеки.

    У3 делится на два, т.к. критический по производительности (майнинг, техническое зрение, цифровая обработка звука) — это не то же, что критические по безопасности (эмуляторы, ядра ОС). Там будут разные (противоречивые) требования.

    У4 тоже делится на два, т.к. есть взаимодействие с железом, это одна часть ОС. А вот низкоуровневые библиотеки и ядро ОС уже надо относить в той или иной части У3, в зависимости от задачи. Если пишем ОС общего назначения, живущую во враждебном мире, то к безопасной части У3. Если ОС спец.назначения (многодорожечный магнитофон или для майнинга), то к опасной части У3. То же и касается низкоуровневых библиотек. Если это библиотека шифрования или сетевой стек, то для неё безопасность важнее производительности.

    Понятно, что реально никто пока не готов признать, что в ОС общего назначения безопасность важнее производительности, однако это всё равно придётся сделать, лучше сработать на упреждение.

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

    * ассемблер (от него всё равно никуда не деться, это очевидно из чтения исходников A2)
    * язык типа активного оберона, точнее, семейство с акцентами в безопасность или скорость.
    * может быть третий вровень, «интер-активный оберон», или «лисп», но я пока в сомнениях — может быть, без него можно обойтись.

    Ответить

    1. Разделение на уровни условное, я тоже 4 года назад думал, что достаточно 3-х уровней, потом коллега Сергей Салищев из СПбГУ предложил 4-х уровневую классификацию. И мы её теперь используем.

      Я думаю, что жизнь покажет. Возможно, будет не 4 языка, а меньше, так что какой-то из уровней будет накрыт не отдельным языком, а диалектом другого языка. Впрочем, возможно, что языков будет больше. Щупать надо…

      Ответить

  7. Поскольку ЯОС перекомпилируется 2 минуты, то это органично вкладывается в концепцию выходного дня. А так пока дождётесь компиляции — уже и выходной закончится. И ещё вопрос — нет ли тут конфликта интересов, Вы же вроде уже занимаетесь разработкой ЯП на работе?

    Ответить

    1. На уровне Тривиля никакого конфликта интересов не может быть, я использую очевидные вещи и свои же наработки по компонентному ассемблеру.

      Ответить

  8. Еще раз перечитал статью. Интенсивное программирование — класснай цель, но как именно мы можем радикально повысить эффективность использования ресурса программистов?
    Я бы сказал, что в статье выдвигается гипотеза — через повторное использование компонентов. Честно говоря, у меня есть сомнения, и вот какие. У нас уже есть хорошие хранилища компонентов, которые не кросс-языковые, иногда не бинарные, а в исходриках (npm), но все же есть. Однако в рамках одного ЯП что-то не похоже, чтобы это помогало перейти к интенсивному программированию, не так ли? По всей видимости есть масса барьеров к еще более широкому повторному использованию, но они не названы, и не сказано, как они будут сниматься. Назову парочку:
    1. Оформить компонент описанием и качественно вести версии — крайне дорого
    2. Знать какие компоненты есть в хранилище — тем сложнее, чем их больше

    Еще можно сказать, что вообще-то в отрасли довольно много всего доступно для повторного использования — библиотеки и фреймворки на техническом уровне, бизнес-платформы на прикладном уровне. Что нам может служить подтверждением того, что глубину повторного использовария действительно можно увеличить еще?

    Ответить

    1. «У нас уже есть хорошие хранилища компонентов, которые не кросс-языковые, иногда не бинарные, а в исходриках (npm), но все же есть.»

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

      А что было бы если мы хотя бы малую часть от освободившихся ресурсов тратили на оформление компонент и их организацию?

      Ответить

    2. Хорошее замечание.
      Можно добавить один слоновий пункт с позиции программиста:
      3. Компоненты сложно переиспользовать, а легко переиспользуемые компоненты сложно создавать.

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

      Ответить

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *