Почему «Глагол»?

«Глагол» — не «Python, переписанный русскими словами». Замысел глубже: взять само устройство русского языка — падеж, вид глагола, согласование — и превратить грамматику в выразительное средство программирования. Там, где другие языки ставят скобки, запятые и служебные значки, русский уже несёт смысл в самой форме слова.

Пометки: есть — работает на площадке; гипотеза — ещё проверяем; эскиз — задуманный синтаксис.

Что даёт грамматика

Падеж задаёт роль аргумента есть

В обычных языках смысл аргумента определяется его местом в скобках: первый параметр — отправитель, второй — получатель, и горе тому, кто их перепутает. В «Глаголе» роль задаёт падеж или предлог, а не позиция. Поэтому переведи сумму от Ивана к Петру и переведи к Петру от Ивана сумму — это одно и то же: слова можно переставлять свободно, потому что «кто», «кому» и «что» помечены прямо в словах. Целый класс ошибок — перепутанные местами аргументы — здесь просто не возникает.

Вид глагола говорит, как выполнять действие есть

Русский глагол несёт не только само действие, но и его характер. Несовершенный вид (помни) в «Глаголе» означает ленивое вычисление — «запомни, но посчитаешь, когда понадобится». Совершенный вид (запомни, сохрани) означает немедленное действие с эффектом. Одно-единственное слово передаёт и то, что делать, и то, когда и как — вместо того чтобы дописывать к коду отдельные пометки вроде @Lazy или async.

Согласование делает разбор устойчивым к ошибкам есть

В русском языке слова согласуются между собой — эта «избыточность» помогает понять фразу, даже если часть её искажена. «Глагол» пользуется тем же: если имя написано в косвенном падеже (владельцу, фамилии), язык связывает его с нужной переменной или полем по общей основе. А неизменяемость значений и отсутствие глобального изменяемого состояния дают потокобезопасность по построению и избавляют от null: вместо «пустой ссылки» — честные типы с вариантами.

Границы внутри программы без цены микросервисов

Это, пожалуй, самая интересная и наименее очевидная сильная сторона «Глагола», поэтому расскажем о ней подробно.

Когда программа вырастает, её хочется разделить на части так, чтобы одна часть не могла запросто залезть во внутренности другой. В обычной Java для этого есть до обидного мало средств: модификатор public открывает класс всему миру, а package-private — всем соседям по пакету. Настоящей, принудительной границы на уровне исходного кода почти нет — договорённости живут в головах разработчиков и в отдельных линтерах, которые легко обойти. Именно поэтому команды часто прыгают сразу к микросервисам: разносят код по отдельным сетевым службам просто ради того, чтобы границу нельзя было нарушить. Но за это приходится платить — сетевыми вызовами, отдельными развёртываниями, распределёнными сбоями и сложностью, которой в едином приложении не было бы.

«Глагол» предлагает середину между этими крайностями. Он делает границу первоклассной и проверяемой прямо в языке, не выходя за пределы одного процесса:

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

Честно: есть ли такое у других? Отдельные кирпичики — да. В Rust есть точная видимость pub(in путь); в языках семейства ML (OCaml, Haskell) модуль публикует строгий интерфейс-подпись; у самой Java с девятой версии есть система модулей (JPMS) с директивами exports … to. Так что сам механизм границ на уровне исходника — не наше изобретение, и честно об этом сказать. Необычно другое: «Глагол» собирает всё вместе — видимость, направленный экспорт, запрет циклов, слои архитектуры и договоры — в единый, встроенный в язык набор, называет его обычными русскими словами и делает полностью необязательным. Пока программа умещается на экране, ребёнок не встречает ни одного из этих понятий; они появляются ровно тогда, когда программа из них вырастает. Такое сочетание доступности и строгости встречается редко.

Статические типы — строгость по желанию есть

«Глагол» — язык с необязательной статической типизацией, и отличает его не сам факт наличия типов, а отношение к ним. Типы не обязательно писать: язык выводит их из кода сам — понимает, что 5 это число, а "привет" строка, — и ещё до запуска предупреждает о явных несоответствиях, например о попытке сложить строку с числом. При желании тип можно указать явно, обычным русским словом типа: число типа Число. А составные типы записываются родительным падежом, как в живой речи, — список чисел, отображение из имени в владельца.

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

Где писать типы — правило простое и одно на весь язык: на границе глагола, то есть на его входе и результате. А что происходит внутри, язык вычисляет сам — тело размечать не нужно, даже большое и сложное. Границы размечены, рутина внутри — нет. Отсюда три вещи, которые нужны настоящему коду: ошибка типов встаёт прямо на границе, где её видно; по одной строке-сигнатуре ясно, что глагол принимает и что отдаёт; и типы доезжают до самого низа — в код для микроконтроллера, где число ложится в регистр без лишних обёрток, ради скорости и надёжности на скудном железе.

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

Контрактное программирование есть

«Глагол» поддерживает программирование по контракту (Design by Contract) — подход, который предложил Бертран Мейер в языке Eiffel. Его суть в том, что отношения между вызывающим кодом и функцией описываются как строгий договор: функция вправе рассчитывать на определённые условия при входе — их называют предусловиями, — и обязана обеспечить определённые условия на выходе, то есть постусловия. Для структур данных к этому добавляются инварианты — свойства, которые должны сохраняться при любых операциях. Мы сознательно называем вещь её общепринятым именем, а не выдумываем своё: за термином стоит зрелая инженерная традиция.

В «Глаголе» контракт записывается родными словами прямо в определении глагола:

прим предусловие и постусловие
утроить число -- это число умножить на 3,
    требует число больше 0,
    гарантирует результат больше число

Слово требует задаёт предусловие — то, что должно быть истинным, чтобы вызов был законным (здесь: число положительно). Слово гарантирует задаёт постусловие — обещание о результате, которое может ссылаться на само вычисленное значение через слово результат (здесь: утроенное положительное число заведомо больше исходного). Важна сама трактовка: нарушение договора — это не «плохие данные», которые надо молча обработать, а ошибка в программе. Либо вызывающая сторона передала недопустимое, либо функция не сдержала обещания. Поэтому при нарушении «Глагол» немедленно останавливается с внятным сообщением, а не продолжает вычисление с уже испорченным значением, пряча причину сбоя далеко от места ошибки.

Ценность контракта не только в защите. Это ещё и документация, которая не может устареть: в отличие от комментария, договор проверяется при каждом запуске, поэтому он всегда описывает актуальное поведение. Здесь контракт смыкается с тестами, которые в «Глаголе» тоже часть языка, а не подключаемая библиотека: инлайн-проверка проверь: X должно равняться Y и таблицы случаев в блоке проверка. Вместе договоры и тесты образуют живую исполняемую спецификацию — описание того, что программа обязана делать, записанное так, что разойтись с кодом оно уже не может.

Контракт естественно связан и с типами. Аннотация типа (число типа Число) — это, по сути, частный, самый распространённый случай предусловия: «здесь ожидается число». Поэтому в «Глаголе» типы и договоры — не два разных механизма, а один и тот же приём разной степени подробности.

Честная граница: сейчас договоры проверяет интерпретатор во время выполнения. Компилятор их опускает — ровно так же, как отключаемые проверки-assertions в других языках: в отладочной сборке они ловят ошибки, в боевой не замедляют работу. Проверять контракты статически, ещё до запуска, — направление на будущее, вместе с развитием системы типов.

Доступность: два стиля и два начертания есть

Одну и ту же программу можно писать в двух стилях. Начинающий стиль — словесный (плюс, больше, равно): его поймёт школьник, и в нём нет ни одного непонятного значка. Профессионал предпочтёт символьный стиль (+, >, =): он короче и привычнее опытному глазу. Это не два разных языка, а две записи над одним ядром — низкий порог входа для детей и лаконичность для опытных сосуществуют без компромисса.

Есть и вторая, независимая ось — начертание, то есть то, какими знаками один и тот же код показывается на экране. У многих операторов два начертания: клавиатурное (простые ASCII-знаки, которые легко набрать руками) и типографское (настоящие типографские символы). Тире и --, умножение × и *, деление ÷ и /, сравнения ≤ ≥ ≠ и <= >= !=, ёлочки «имя» и лапки "имя", подстрочные индексы x₁ и x_1 — это синонимы над одним ядром. Хранится код всегда в клавиатурном каноне (чтобы файл легко набирался и не зависел от раскладки), но площадка умеет показать его в типографском виде — переключателем «Вид».

Зачем это особенно важно в паре с ИИ. Человеку удобнее набирать простые знаки с клавиатуры, а вот при чтении — и человеку, и языковой модели — естественнее видеть настоящие типографские символы: «имя» в ёлочках вместо кавычек-палочек, вместо <=, x₁ с подстрочным индексом. Поэтому «Глагол» разделяет ввод и показ: пишешь как удобно руками, а агентское начертание переводит это в человекочитаемые типографские знаки, не меняя ни буквы смысла. Один и тот же код получает два лица — клавиатурное для набора и агентское для чтения.

Один язык — любой бэкенд

Здесь важно поправить возможное недопонимание: «Глагол» задуман не как «язык для JVM с довеском», а как единый язык, который проецируется на любую платформу. В основе — одно дерево разбора (единое представление программы). А уже из него разные «выходы» порождают исполнение под конкретную цель:

Замысел прост: один язык на все случаи жизни. Программу, написанную по-русски, не нужно переписывать под каждую платформу — меняется лишь то, во что её переводят при сборке. У разных целей будут свои особенности (на микроконтроллере, например, типы придётся указывать строже — иначе не собрать быстрый и компактный код), но язык остаётся одним и тем же.

Работа с ИИ-агентами

«Глагол» создан удобным для пары «человек + ИИ-агент». Роль аргумента задаёт падеж, а не позиция — перепутать отправителя с получателем структурно нельзя: «кто» и «кому» помечены в самих словах. Смысл — падеж, вид, предлог — стоит рядом с аргументом, а не разбросан по коду, поэтому агент удерживает меньше дальних связей и экономит контекст.

Для агента есть компактный файл-навык (SKILL.md): он умещается в контекстное окно, и с ним агент в наших прогонах решал задачи уровня LeetCode Hard. Код читается как связное описание задачи по-русски — и человек, и агент проверяют его без мысленного перевода с латиницы.

Компактность кода

«Глагол» стремится быть компактным — не за счёт нагромождения значков, а за счёт грамматики. По ощущению объём и «синтаксический шум» сопоставимы с Kotlin, но строгих замеров пока нет: числовое сравнение с другими языками — это отдельная задача, которую мы честно держим в планах, а не выдаём за сделанное.

За счёт чего экономится место:

Живой пример — факториал есть

Этот код запускается на обоих бэкендах прямо сейчас:

// Java
static long factorial(long n) {
    if (n < 2) return 1;
    return n * factorial(n - 1);
}
прим Глагол
факториал числа -- это если число
  меньше 2, то 1, иначе число
  умножить на факториал (число минус 1)

Имя «факториал числа» читается как естественная фраза — существительное с родительным падежом, без скобок вокруг параметра. И проверку можно написать тут же: проверь: (факториал 5) должно равняться 120.

Настоящий бэкенд «Клиники» — не игрушечный есть

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

прим Глагол — реальный бэкенд /клиника
сущность «Владелец»:
  ключ ид
  имя — строка обязательно                 прим @NotBlank
  телефон — строка только цифры            прим @Pattern(\d+)
  питомцы — много «Питомец» по владельцу   прим @OneToMany(mappedBy)

хранилище «Владельцы» хранит «Владелец»:
  найти по началу фамилии                  прим findByФамилияStartingWith

приложение «Клиника»:
  встрой владельцы — «Владельцы»            прим @Autowired

В Java для того же понадобились бы тяжёлые классы, обвешанные аннотациями (@Entity, @Table, @Id, @Column, @ManyToOne, @JoinColumn), геттеры, сеттеры, конструкторы и интерфейсы Spring Data. «Глагол» прячет весь этот инфраструктурный шум за родными словами (обязательно — это @NotBlank, только цифры — @Pattern, встрой — @Autowired), а на выходе порождает ровно тот же самый Spring и JPA.

Уточним честно: «компактнее в разы» — это про декларативный слой данных и инфраструктуры (сущности, репозитории, внедрение, проверки), где Java требует особенно много шаблонного кода. Собственно бизнес-логика короче не обязательно «в разы». Но именно этот инфраструктурный слой в корпоративных приложениях огромен — и выигрыш здесь настоящий и проверяемый: перед вами работающая /demo, целиком порождённая из русского исходника.

Аналог Java Stream API — по-русски есть

Обработку коллекций «Глагол» строит как Java Stream API, но без точек и лямбд: шаги конвейера связывает родительный падеж, а функцию глагол получает творительным. Жадная часть Stream API уже работает — отфильтруй/отобрази/сверни (filter/map/reduce), упорядочение (sorted), первые (limit), любой/все (anyMatch/allMatch), сцепление … действием (flatMap), группировка/подсчёт (collectors):

// Java (Stream API)
ages.stream()
  .filter(a -> a > 17)
  .sorted()
  .limit(3)
  .toList();
прим Глагол — то же, конвейером
первые 3 (упорядочение (отфильтруй возрасты взрослым))
прим  где  взрослый число -- это число больше 17

А частотный анализ, где в Java нужны Collectors.groupingBy + counting + max, у нас — одна русская фраза мода слов текста (пример №108, №106).

Честная ленивость и бесконечные потоки — уже есть. Есть отдельный тип поток — ленивая, потенциально бесконечная последовательность (аналог Stream.iterate и ленивого limit): первые 10 (отфильтровывай натуральные, оно простое), решето Эратосфена, ряды. Ленивость несёт вид глагола (несовершенный = ленивый шаг, совершенный терминал материализует) — без .stream()/.collect(). Работает на трёх бэкендах; примеры на площадке (потоки, решето).

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

Где «Глагол» может проигрывать

Если мерить компактность длиной отдельных слов, русские слова обычно длиннее английских: сохрани изменённый баланс заметно длиннее, чем saveBalance. Сила «Глагола» не в коротких словах, а в плотности смысла на строку: грамматика берёт на себя работу синтаксического сахара. Это утверждение ещё предстоит подтвердить числами — сравнение объёма и числа лексем с другими языками мы завели отдельной задачей и не выдаём результат заранее.