Девять лет назад я написал эти строки* и совершенно забыл про них через некоторое время. Наткнувшись на свои заметки в процессе написания статьи, неожиданно нашел там подходящий эпиграф.
Пройдет не менее пяти лет, прежде чем нейронные сети выйдут на уровень понимания, достаточный для интерпретации законченных фрагментов кода. Это поднимет очередную волну технологического прогресса и наступление следующих этапов применения машинного обучения в разработке не заставит себя ждать. В конечном итоге нейронные сети научатся генерировать код.
Привет! С 2016 года прошло 9 лет, и я решил провести эксперимент по проектированию и созданию специализированного языка программирования с условием не писать самому ничего, кроме промптов. Мне удалось, используя ChatGPT и впоследствии Codex, за пару месяцев плотной работы по вечерам и субботам спроектировать язык (кодовое имя Branchline), написать на него интерпретатор, компилятор, виртуальную машину, а затем перевести это всё на Kotlin Multiplatform (KMP), чтобы получить версию под JS для онлайн-песочницы.
Disclaimer
Статья написана мной без использования ИИ-помощников.
Вся работа достуна на github и sourcecraft под Apache License 2.0. На текущий момент я использую гитхаб как основной репозиторий.
Если вдруг появится желание помочь обратной связью/сценариями использования, я буду рад и открыт к обсуждению.
История эксперимента
Мой Kotlin бекграунд — R&D в semantic web и построение прототипов разной степени детализации около 10 лет. За это время довелось поработать над разными задачами так или иначе относящиеся к интеграции данных и работе с большими графам. Но эта часть моей жизни осталась в прошлом и сейчас я как техлид работаю над созданием МДМ систем в компании тесно связанной с российским автопромом.
Изначально идея языка и его первый прототип появились в результате исследования в пользу дружественной компании в августе 2025. К сожалению, R&D был приостановлен досрочно, но я настолько загорелся идеей, что почти все свободные вечера в течение сентября работал над его публичной версией.
Хотелось создать то, что способно работать в low-code системах. До этого приходилось крутиться по-разному, но наиболее приближенное к промышленным условиям оказалась JSONata, соответственно, относительно неё я и ставил цели. Решение должно быть:
Human-friendly. В частности, быть читаемым для тех, кто знаком с SQL и программированием на базовом уровне (JS/Python). Также программы должны поддаваться отладке — в идеале это пошаговое выполнение и возможность отследить изменения значений переменных.
AI-friendly. Код на языке Branchline должен быть оптимизирован для генерации агентами. Материалов тут очень мало, но выглядит так, что для этого важно, чтобы синтаксис был однозначен и язык хорошо ложился на общепринятые абстракции.
Быстровыполнимым. Некоторые JSONata-скрипты на несколько тысяч строк выполнялись критично медленно.
Безопасным. Программы на Branchline не должны иметь возможность повредить хост-систему.
Меня очень тянуло в сторону forth-like и lisp-like языков из-за простоты их реализации, но я быстро прекратил эти эксперименты под влиянием первого пункта. Тогда я запустил несколько deep research в ChatGPT, прочитал пачку статей и решил, что лучше сделать контекстно-независимый язык со знакомыми конструкциями и искусственным ограничением сложности выражений. Задача была в том, чтобы человеку и модели было сложно создать сложное для понимания выражение для определенной выше целевой аудитории. Как замерить эффект синтаксиса на воспринимаемость языка — отдельное интересное направление исследований.
Сначала я хотел сделать сразу и язык преобразования, где описание единичного преобразования начинается со слова TRANSFORM, и конструкции для композиции преобразований, в идеале, чтобы можно было сразу собрать DAG. Я планировал выжимать максимум производительности и использовать только стриминговую модель чтения JSON (dsl-json). Очень быстро все эти желания ушли на второй план, и моей основной проблемой стало определение, что язык должен предоставлять пользователю, а что должно остаться исключительно на уровне движка исполнения. Я несколько раз корректировал синтаксис языка (например, простым промтом отменил обязательность точки с запятой и ввёл trailing comma), генерировал unit-тесты и сравнивал производительность с JVM-реализацией JSONata, интегрировал язык с OWL-онтологиями и JSON-схемой, экспериментировал с типами данных и много ещё с чем. В итоге DAG оказался за бортом, а JSON разбирается самым простым образом — через kotlinx.serialiazation.json.
Такая совместная с ИИ работа стала для меня полигоном для испытаний, возможностью подумать над автоматическим выводом типов, способом оптимального размещения JSON-like объектов в памяти стековой VM и о том, как правильно составлять запросы для ИИ-агента. Я планирую получить готовый для промышленного использования язык, который выразительнее и удобнее специализированных языков преобразования данных, существующих сегодня.
Об языке Branchline
Так я пришел к тому, что нужно стремиться к как можно меньшему числу специальных символов.
LET key = "okak"
// ещё одна маленькая радость – наличие list comprehensions
LET names = [ user.name FOR EACH user IN row.users WHERE user.active ]
LET obj = {
1: "целые неотрицательные числа могут быть ключами",
message: "ключи не обязательно помещать в кавычки",
"но можно и закавычить": names,
[key]: "если ключом объекта является переменная, она оборачивается в []",
}
Функции являются объектами первого класса:
LET sum = (x, y) -> x + y
LET inc = (x) -> APPLY(sum, x, 1)
OUTPUT { answer: inc(41) }
Дальше — вопросы IO и общей памяти. Тут я не придумал ничего лучше, чем отдать эту функцию хост-системе, а на уровне языка дать возможность объявлять «общие» структуры. Делается это так:
SHARED cache SINGLE; // ключ может быть записан только один раз, параллелить такое сильно проще
SHARED buf MANY; // ключ может быть перезаписан сколько угодно раз, привет блокировки
Далее в программе можно читать и писать в общую память, как будто это локальная переменная, а виртуальная машина уже будет предоставлять реализацию — Caffeine, Redis или что-то иное.
Также есть элементы dataflow-программирования. Например, конструкция SUSPEND cache.key скажет виртуальной машине, что программу можно остановить и возобновить, как только в кеше появится нужный ключ. Это сильно экспериментальная часть языка, и я пока не прорабатывал её детально. Особенно хочется поиграться с ленивыми вычислениями (thunk) — это когда кусок кода выполняется по мере поступления в него всех необходимых зависимостей. Когда-то мне довелось использовать язык Oz, и меня особенно впечатлило, что выражение thread A = B + 1 выполнится даже если в текущей зоне видимости нет B, то есть эта запись означает примерно следующее: как только в зоне видимости появится B, инкрементируй его и положи в A, а пока спи. Фишка в том, что в виртуальной машине Branchline мы могли бы выполнять операции с неизвестными переменными до тех пор, пока не появится явная необходимость их разрешения. Это выглядит довольно перспективно, так как позволяет в ряде случаев оптимизировать вычисления и сократить общее время выполнения программы.
Пара слов про отладку. Интерпретатор и ВМ поддерживают отладочный режим и в нём программно можно получить всю историю изменения данных или явно запросить её в программе через функцию EXPLAIN:
LET subtotal = msg.unit_price * msg.quantity;
LET discount = msg.discount ?? 0;
LET total = subtotal - discount;
OUTPUT {
order_id: msg.order_id,
subtotal: subtotal,
discount: discount,
total: total,
explain_total: EXPLAIN("total")
}
EXPLAIN генерирует машиночитаемый трейс, а для человека есть вывод в строку:
total
→ LET
calc:
subtotal = 105
discount = 5
(105 - 5) -> 100
→ OUTPUT
calc:
total -> 100
• order_id
→ OUTPUT
calc:
msg.order_id -> "A-1024"
• subtotal
→ LET
calc:
msg.unit_price = 35
msg.quantity = 3
(35 * 3) -> 105
→ OUTPUT
calc:
subtotal -> 105
• discount
→ LET
calc:
msg.discount = 5
(5 ?? 0) -> 5
→ OUTPUT
calc:
discount -> 5
Посмотреть вживую можно в playground
Наличие такого анализа приводит к интересным возможностям, а именно — автогенерации контрактов, быстрой валидации схем и CI-проверок сложных вычислений. Становится возможным сгенерировать требования к входу (например в виде json-схемы) и схему данных на выходе. В свою очередь для DAG-графа можно автоматически проверять его корректность даже не фиксируя промежуточные DTO — это должно позволить быстрое прототипирование и возможность автодокументации сложных вычислений и возможность проверять корректность изменений в CI. Получается что-то типа встроенного контрактного тестирования, только без необходимости писать контракты — текст трансформации сам по себе является таким контрактом.
В остальном — это довольно типичный язык. Планирую сделать его синтаксически ещё проще, написать ряд нагрузочных тестов и все-таки написать рантайм, который мог бы выполнять DAG из branchline-скриптов на кластере. Но это сильно в будущем. Относительно скорости работы — полное исследование я не проводил, но для частного интересного мне случая (несколько тысяч строк в коде преобразования) я получил прирост скорости выполнения на ВМ в 30 раз относительно java-реализации JSONata и примерно x100 если выполнять через интерпретатор, который пока работает быстрее.
ChatGPT уже сейчас вполне сносно справляется генерировать скрипты обработки данных, вот например обработка XML с результатами выполнения тестов в github workflow, которая нужна, чтобы генерить красивые плашечки в README. Результат мне эстетически не нравится, но это исправимо. Зато работает и это радостно.
Выводы
Их будет три. Первый — касательно текущего состояния дел, глазами инженера из terra non grata. После эксперимента я стал больше использовать AI в работе и сейчас, когда я вижу как задачи средней технической сложности решаются людьми, то испытываю примерно то же чувство, когда вынужден писать от руки длинный текст. Я до сих смеюсь с шутки про "Михалыч, врубай насос" — отсылки к тому, что разработчики-старожилы через несколько лет будут единственный спасением от навайбленного кода. Но глубоко убежден, что это скорее попытка успокоиться, чем пошутить — не вижу ни одной причины, почему отрасль должна остаться в том виде, в котором она была до LLM. Лично мне всегда было жаль времени, которое забирает физическое набивание кода, часто это ещё и довольно скучное занятие. И то, что некий сервис будет делать это на порядки быстрее, дешевле и, вполне вероятно, качественнее — это прекрасно. Да и с точки зрения бизнеса, возможность драматического ускорения разработки относительно роста расходов сложно переоценить, а значит, так и будет.
Второй — про то, легко ли написать свой специализированный язык. Ответ — да, если знать где остановиться. По моим представлениям, если не заморачиваться с виртуальной машиной и использовать проверенные подходы, то 80% времени уйдёт на дизайн, 10% на документацию и тестов и 10% на непосредственно кодирование. Если язык планируется использовать для решения практических задач, то ошибки в дизайне будет самыми дорогими, поэтому важно попытаться их предотвратить на раннем этапе. Создавать DSL можно и нужно, т.к. правильно подобные абстракции существенно упростят и ускорят разработку и сопровождение.
Третий — касательно использования KMP, а не генерирования нативного кода для каждой платформы — тут я пока не определился. С одной стороны, бесплатно получать версии под js/wasm/jvm/native привлекательно, с другой стороны, не так уж это и бесплатно — под JS нужно тащить весь котлиновский рантайм и даже после DCE та же JSONata ощутимо лидирует (75kb vs 440kb). Но пока преимущества единой кодовой базы сильно перевешивают недостатки.
А ещё, я лично жду момента, когда решения вида spring-* канут в лету за ненадобностью, а их код будет будет генерироваться на лету под конкретный проект. Тогда заживём можно будет полностью сконцентрировать на описании дизайна и свойств проектируемых систем.
Спасибо за внимание и stay tuned!
impwx
Идея интересная, но ниша не вполне понятна. Если не-технарю понадобится написать скрипт, попросить ИИ в свободной форме написать это на любом популярном языке будет гораздо проще, чем разбираться со специализированным решением без тулинга\библиотек\сообщества.
0ex0 Автор
Ниша узкая, это правда. Но например, если мы непременно хотим иметь возможность менять скрипт на лету и при этом иметь нечто более специализированное чем python, то у нас не так много опций — JSONiq, JSONata, JOLT, JSLT и ещё что-то редкое. У всего этого есть минусы, а если мы добавим полноценную отладку и экзотику типа вывода контрактов, то я даже не знаю подходящих альтернатив.
Но Branchline — это прежде всего эксперимент в рамках другого эксперимента. По-настоящем, как мне кажется, его можно раскрыть только если будет реализация платформы.