Перейти к содержанию
Публикация AiManual

Как ИИ-агент участвует в проектировании языка: 11 споров с Claude о синтаксисе Nova

Автор языка Nova разбирает 11 споров с Claude о синтаксисе: эффекты, массивы, объявление методов, модификаторы указателей. Где агент защищает записанное по инер

Коротко

Что будет в материале

  1. 01

    Зачем вообще спорить с ИИ о синтаксисе

  2. 02

    Спор №1: восклицательный знак для эффектов и почему он не выжил

  3. 03

    Спор №2: массивы [T] и ловушка согласованности с дженериками

  4. 04

    Спор №3: объявление метода за пять раундов

Nova компилируется в C, имеет сборщик мусора и рантайм M:N: лёгкие задачи поверх потоков ОС, как горутины в Go. Это язык для бэкенда и серверных программ, похожий на Go и Rust, и его автор проектирует вместе с ИИ-агентами, в основном с Claude. Одиннадцать случаев, когда пришлось спорить о синтаксисе, он разобрал в отдельной статье.

Ключевая особенность Nova - эффекты. Функция прямо в сигнатуре объявляет, что делает с внешним миром: работает с сетью, часами, файлами, может завершиться ошибкой. Реализацию эффекта можно подменить в рантайме, например в тестах. Запись эффектов стала первым полем боя, и на ней хорошо видна разница между логикой агента и вкусом человека.

Роли распределены жёстко. Автор задаёт направление, оценивает, как строка читается, и принимает финальное решение. Claude предлагает варианты, обосновывает их и помнит уже принятое. Агент пишет и помнит, человек спорит и выбирает. Почти все конфликты растут из этого деления: то, что агент запомнил, он же и защищает.

Зачем вообще спорить с ИИ о синтаксисе

Делегировать проектирование языка агенту целиком не получается. Модель выдаёт варианты быстро, но выбор между ними упирается в критерий, который живёт не в тексте обоснования, а в голове того, кто будет читать код через год.

Кто что делает: роли человека и агента в проекте Nova

Автор держит видение языка и вкус: каким должен быть Nova, что в нём уместно, а что превратит его в чужой язык с другим именем. Он же говорит «нет», и делает это часто. Claude отвечает за перебор: предлагает варианты синтаксиса, подводит под них обоснования, помнит, что уже записано в спецификации.

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

Правило автора: всегда дать агенту аргументированно изложить идею и принять её, если она хороша и согласуется с видением. Он отмечает, что почти каждый раз итог оказывался лучше того, что Claude защищал вначале. Спор нужен не для победы над моделью, а для проверки собственного решения на прочность.

Почему синтаксис - особенно неудобная задача для LLM

ИИ рассуждает логически: «символ свободен», «парсер однозначен», «согласовано с дженериками». Человек смотрит на строку и видит, как она читается с первого взгляда. Этого ИИ пока не умеет.

Логическая непротиворечивость и читаемость - разные критерии, и агент оптимизирует первый. Проверить, что символ нигде не занят, легко. Проверить, что !throws не читается как отрицание, можно только глазами и опытом чтения чужого кода.

Отсюда механика большинства из 11 споров: агент выдаёт локально корректное обоснование, автор отвечает фразой о том, как это выглядит на странице, и раунд закрывается. Спор идёт не о фактах, а о критерии оценки.

Спор №1: восклицательный знак для эффектов и почему он не выжил

В одной из ранних версий синтаксиса Claude предложил помечать эффекты восклицательным знаком:

fn save(u User) !throws io async -> ()

Обоснование было логичным: ! свободен, ни для чего важного не используется, похоже на Swift. Проверка на занятость символа пройдена, аналогия найдена, предложение готово к принятию.

Автор ответил одной фразой: «!throws выглядит как not». Claude согласился почти сразу. Эффекты стали обычными словами между ) и ->, без дополнительных маркеров:

fn save(u User) Throws Io Async -> ()

Потом язык менялся ещё. Throws переименовали в Fail, а Async убрали из системы типов: асинхронность стала делом рантайма M:N. Сигнатура сейчас может выглядеть так: Fs Fail[IoError]. Принцип сохранился: эффекты пишутся словами, сигнатура читается без расшифровки.

Вывод для читателя простой. Аргумент «символ свободен» отвечает на вопрос занятости символа, а не на вопрос читаемости. Одна фраза о том, как строка выглядит, обнулила логичное обоснование, потому что била в критерий, который агент не проверял.

Спор №2: массивы [T] и ловушка согласованности с дженериками

Сначала массивы в Nova писались как [T], в стиле Rust и Swift. Claude защищал запись двумя аргументами: согласовано с дженериками Option[T] и HashMap[K, V], парсер однозначен.

В этом обосновании смешаны два разных источника. Option[T] и HashMap[K, V] - собственные дженерики Nova, а [T] для массива - чужая конвенция, перенесённая из Rust и Swift. Внешнее сходство подано как внутренняя согласованность проекта, хотя связь здесь ассоциативная. Агент взял привычную запись из языков, на которых учился, и назвал её логикой Nova.

Однозначность парсера тоже узкий критерий. Он отвечает на вопрос «можно ли разобрать строку», а не на вопрос «поймёт ли её человек с первого взгляда». Большинство плохих синтаксисов прекрасно разбираются парсером.

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

Спор №3: объявление метода за пять раундов

На синтаксис объявления метода понадобилось пять раундов. Это хороший ориентир по стоимости: длинный спор о форме записи - нормальная часть работы, а не признак плохо сформулированного промпта.

Динамика в таких раундах повторяется. Агент защищает уже записанное решение и подводит под него новые доводы. Автор приводит контрпримеры: как объявление читается рядом с другими конструкциями, где теряется граница между именем метода, получателем и возвращаемым типом. Раунды нужны, чтобы отделить «логично» от «читается». Конкретные варианты синтаксиса в источнике не раскрыты, но механика видна именно на длине: одного хода не хватило, потому что каждый ответ агента опирался на предыдущий, а не на новый критерий.

Практический вывод: если спор затянулся, стоит сменить тип аргумента. Ещё один довод в пользу прежнего варианта ничего не даст, а пример чтения строки или прецедент из другого языка меняет рамку обсуждения.

Спор №4: модификаторы указателей и где останавливается неизменяемость

Иногда Claude переубеждает автора, в том числе в вопросе о том, где останавливается модификатор неизменяемости перед указателем.

Вопрос тонкий по своей природе. Модификатор неизменяемости рядом с указателем может относиться к самому указателю или к значению, на которое он смотрит. В C и C++ это различие задаётся позицией const относительно звёздочки, в Rust - двумя разными типами ссылок. Ошибка на один символ меняет смысл объявления и ломает привычки, которые разработчик принёс из другого языка.

Детали аргументации в источнике не приведены, подтверждён сам факт: агент переубедил автора. Ценность кейса в другом. Здесь спор идёт о семантике, а не о свободном символе, и у модели есть настоящее преимущество: она держит в голове больше вариантов того, как похожие языки решают ту же задачу.

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

Остальные споры: что ещё пришлось отстаивать

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

КейсАргумент ClaudeЧто возразил авторИтог
Эффекты! свободен, похоже на Swift«!throws выглядит как not»Эффекты словами между ) и ->, позже Fail вместо Throws
МассивыСогласовано с Option[T] и HashMap[K, V], парсер однозначенСмешаны конвенции Rust и Swift с дженериками NovaОбсуждалось и менялось, финальная запись в источнике не приведена
Объявление методаЗащита ранее записанного вариантаКонтрпримеры на чтение строкиПять раундов, детали не раскрыты
Префиксы памяти ~TРешение, предложенное самим агентом«На Go написан Kubernetes»Отказ от префиксов в пользу сборщика мусора
Модификатор неизменяемостиАргумент о семантике указателяАвтор согласилсяClaude переубедил автора

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

Три типичные ошибки Claude в дизайне синтаксиса

  1. Защита уже записанного решения. Claude неделями защищал отказ от префиксов памяти ~T в пользу сборщика мусора, решение, которое сам когда-то предложил, и сдался только перед прецедентом из реального мира. Память агента работает против пересмотра: собственное предложение в контексте выглядит как обязательство.
  2. Смешивание конвенций разных языков. Восклицательный знак пришёл из ассоциации со Swift, [T] для массива - из Rust и Swift. Чужая привычка подаётся как согласованность проекта, хотя проект состоит из собственных решений.
  3. Слепота к чтению с первого взгляда. Агент проверяет занятость символа и однозначность парсера, но не проверяет, что увидит человек. Фразы «!throws выглядит как not» хватило, чтобы обнулить аккуратное обоснование.

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

Когда прецедент весит больше логики: урок с ~T и Go

Claude неделями защищал решение об отказе от префиксов памяти ~T в пользу сборщика мусора, которое сам когда-то предложил, и сдался только перед прецедентом из реального мира - аргументом «на Go написан Kubernetes» (история автора Nova).

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

Практический вывод: в споре с агентом искать прецедент, а не наращивать цепочку доводов. Прецедент должен быть релевантен задаче, иначе получится подмена аргумента авторитетом. Для решения об отказе от ручного управления памятью в серверном языке Go и Kubernetes релевантны: та же область, тот же класс нагрузок.

Чек-лист: когда настаивать на своём, а когда соглашаться с агентом

Чек-лист собран по разобранным кейсам. В исходном тексте он анонсирован, но его содержание в доступном фрагменте отсутствует, поэтому пункты выведены из описанных споров.

Настаивать на своём, если:

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

Соглашаться, если:

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

В любом случае давать агенту аргументированно изложить идею до конца. Автор отмечает: почти каждый раз итог оказывался лучше того, что Claude защищал вначале. Это эвристика, а не гарантия. Финальное решение остаётся за человеком, и ответственность за читаемость языка тоже на нём.

Что это значит для практики работы с Claude в разработке

Кейс Nova переносится на обычную разработку с Claude в роли помощника по коду. Агент полезен как генератор вариантов и держатель контекста: он помнит, какие решения приняты, и предлагает следующий шаг в их рамках. Арбитром вкуса он не работает, потому что критерий читаемости у него отсутствует.

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

Тот же рисунок встречается в других проектах с агентами. В оркестраторе задач на Claude Code разработчик упёрся в кризис код-ревью и пересмотр метрик (разбор архитектуры). В кейсе с Jira виртуальные исполнители берут рутину, а ревью остаётся за человеком (настройка доступов и ограничения). Общий фон понятен: код дешевеет, а решения дорожают, о чём говорит и отказ Hacktoberfest 2026 считать пул-реквесты.

Ограничения стоит назвать прямо. Это опыт одного проекта, одного автора и одного агента, без замеров времени и доли сгенерированного кода. Переносить выводы на другую задачу нужно с поправкой на контекст: в проектировании языка спор о читаемости, в прикладной разработке чаще спор о поведении системы. Общее одно: агент выигрывает там, где нужен перебор вариантов, и проигрывает там, где нужен вкус.

Подписаться на канал