Вычисления и вывод: = против :
Главное правило Aura, отличающее её от YAML/JSON (аналог locals vs outputs в Terraform):
| Форма | Смысл | В выводе? |
|---|---|---|
x = expr | приватная переменная | нет |
key: expr | свойство | да |
domain "name" ... end | блок | да (ключ = имя) |
base = 8000 # вычисление
port: base + 80 # вывод: {"port": 8080}
Благодаря этому анализ мёртвого кода точен: неиспользуемая =-переменная —
всегда настоящий мусор (W0501), а не «может, это чей-то вывод».
Иммутабельность
Повторное присваивание в одной области — ошибка:
x = 1
x = 2 # E0301: variable 'x' is immutable
Явное затенение
Переопределить внешнюю переменную во вложенном блоке можно только явно:
log_path = "/var/log/app.log"
domain "debug"
log_path = "/tmp/debug.log" # E0302: требуется shadow
shadow log_path = "/tmp/debug.log" # ок — намерение видно в diff'е
path: log_path # → "/tmp/debug.log"
end
«Почему в проде другой путь?» больше не тихий сюрприз — затенение всегда
видно по слову shadow.
Длинные числа
Разделяйте разряды через _ там, где это помогает глазу:
max_bytes: 10_000_000
retention: 2_592_000
port: 8_080
rate: 0.000_001 # в дробной части группы отсчитываются от точки
Это полностью необязательно: 2371933 и 2_371_933 — одно и то же число, и
никто не требует что-либо группировать.
Но если вы группируете, разделитель обязан разбивать по три, считая от
десятичной точки. Поэтому короткая группа оказывается в начале целой части и в
конце дробной: 8_080, 1.123_45.
Это строже, чем в Go, Ruby или Rust, где 2_3_7_1_9_3_3 допустимо. Причины две.
Разделитель, который может стоять где угодно, — это второй способ записать одно
и то же число, а язык тратит свои правила ровно на то, чтобы таких способов не
было. И он вводит в заблуждение: 1_0000_000 — это десять миллионов, но всякий,
кто читает по три, увидит куда большее число.
100_, 1__0 и 1_.5 — тоже ошибки: разделять там нечего.
Всё это существует потому, что длинные числа живут именно в конфигурации, а просчитанный ноль — та ошибка, которую больше ничто не поймает: значение корректно, тип верен, и сервис тихо получает в десять раз больше, чем должен.
Строки и интерполяция
name = "auth"
image: "company/#{name}:v#{1 + 1}" # → "company/auth:v2"
Внутри #{...} — обычный синтаксис выражений: кавычки вложенных строк
не экранируются ("#{list.join(", ")}" — валидно).
У null нет текстовой формы, поэтому его интерполяция — это E0324, а не
слово null:
host = env("HOST", null)
url: "https://#{host == null ? "localhost" : host}/api"
Проверка написана явно, потому что какой текст подставить вместо отсутствующего
значения — это решение о конфигурации. Если бы печаталось null, эти четыре
буквы попали бы в вывод, и тот, кто читает его дальше, увидел бы настройку, а не
её отсутствие. А обычная причина, по которой null оказывается в строке, — это
значение, потерянное выше по цепочке.
Доступ к данным
Точка — для полей, скобки — только для индексов списков:
version: cfg.package.version # обычные ключи
special: cfg."ключ с пробелом" # любой ключ — строкой
dynamic: cfg.envs."#{region}" # динамический ключ
first: apps[0].name # индекс (за границами — E0317)
optional: cfg.get("maybe", "default") # промах → default, не ошибка
Опечатка в имени поля — ошибка E0308 с точной позицией, а не молчаливый null.