Исполняемые спецификации на flang Правила, порядок и свойства: как считается решение
0%

Правила, порядок и свойства: как считается решение

Правила, порядок и свойства: как считается решение

Код в этой главе записан в прежней поверхности языка — со словами категория, объект, утилита. Сегодняшний компилятор её слова читает, но программой такой файл не считает: файл, где есть только утилиты, flang check отклоняет. Разбор задачи в главе верен; синтаксис переносится по таблице из главы «Старые модели».

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

Минимальный пример

категория «Продажи»

  объект Покупка
    сумма является деньгами

  утилита «Рассчитать скидку»
    принимает Покупка
    возвращает деньги
    начинает с 0

    правило «Большая покупка»
      если сумма не меньше 10000
      то добавить 10 процентов от поля сумма

    пример «Ниже порога»
      дано сумма равна 5000
      ожидается результат равен 0

    пример «Выше порога»
      дано сумма равна 20000
      ожидается результат равен 2000

Объект описывает форму входа. Утилита называет вход (принимает), тип результата (возвращает) и обязательное начальное значение (начинает с). Без него результат не появляется из неявного null или 0 по умолчанию рантайма — ноль в примере выше написан автором модели, а не подставлен компилятором.

Что делает компилятор

  1. Естественный синтаксис разбирается в канонический FtsDocument — один и тот же формат читают validate, интерпретатор и генератор TypeScript.
  2. validate проверяет структуру: у каждого правила есть условие и действие, у каждого свойства — одно сравнение результата, ссылки на поля существуют в объекте принимает.
  3. При выполнении (executeUtility или сгенерированная функция) результат получает начинает с.
  4. Правила проходятся сверху вниз; для каждого проверяется условие.
  5. У правил с истинным условием выполняется действие: то результат равен заменяет результат, то добавить прибавляет к нему число.
  6. После всех правил по порядку проверяются все свойства.
  7. Если свойство нарушено, выполнение останавливается с диагностикой FTS_UTILITY_PROPERTY — значение наружу не уходит.
  8. Если все свойства выполнены, вызывающая сторона получает готовый результат.

Эта последовательность одна и та же для интерпретатора и для сгенерированного fts.utilities.ts: генератор не имеет права оптимизировать порядок правил или пропустить проверку свойства.

Правила — это не if/else

Выполняются все правила, чьи условия истинны, в порядке объявления. Это принципиальное отличие от цепочки if / else if, где сработавшая ветка исключает остальные:

категория «Продажи»

  объект Покупка
    сумма является деньгами
    «постоянный клиент» является признаком
    промокод является строкой

  утилита «Рассчитать скидку»
    принимает Покупка
    возвращает деньги
    начинает с 0

    правило «Большая покупка»
      если сумма не меньше 10000
      и сумма не больше 100000
      то добавить 10 процентов от поля сумма

    правило «Постоянный клиент»
      если «постоянный клиент» равен да
      и сумма больше 0
      и сумма не больше 100000
      то добавить 5 процентов от поля сумма

    правило «Промокод на крупный заказ»
      если сумма не меньше 15000
      и сумма не больше 100000
      и промокод равен «ЛЕТО2026»
      то добавить 7 процентов от поля сумма

    правило «Очень крупная покупка»
      если сумма больше 100000
      то добавить 22000

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

    пример «Обычная покупка»
      дано сумма равна 5000
      дано «постоянный клиент» равен нет
      дано промокод равен «НЕТ»
      ожидается результат равен 0

    пример «Крупная покупка без промокода»
      дано сумма равна 20000
      дано «постоянный клиент» равен нет
      дано промокод равен «ЗИМА»
      ожидается результат равен 2000

    пример «Крупная покупка постоянного клиента с промокодом»
      дано сумма равна 20000
      дано «постоянный клиент» равен да
      дано промокод равен «ЛЕТО2026»
      ожидается результат равен 4400

    пример «Покупка ровно на потолок скидки»
      дано сумма равна 100000
      дано «постоянный клиент» равен да
      дано промокод равен «ЛЕТО2026»
      ожидается результат равен 22000

Для примера с двадцатью тысячами срабатывают три правила из четырёх: 10 % + 5 %

  • 7 % от 20 000 дают 4 400. Ни одно правило не «отменяет» другое — они складываются, потому что каждое из них прибавляет к общему результату.

Потолок скидки записан числом (результат не больше 22000), а не процентом от поля, и границы по сумме названы прямо в условиях правил. Свойство проверяется на всей области входа, а не только там, где сработали правила: предел вида 25 процентов от поля сумма уходил бы в минус на отрицательной сумме и ронял расчёт там, где ни одно правило не сработало. Подробный разбор — в главе об антипаттернах.

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

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

Сравнения и проценты

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

N процентов от поля X вычисляется от значения поля X во входных данных, а не от текущего результата. Имя поля указывается ровно так, как оно объявлено в объекте, без падежей: 10 процентов от поля сумма, 2 процента от поля «страховая сумма». Компилятор не пытается склонять русские существительные — это осознанное ограничение, а не недоработка.

Свойство — постусловие, а не автокоррекция

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

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

Свойство пишется как одно сравнение результата — результат <сравнение> <выражение>. Выражением может быть число, значение поля или процент от поля, как в примере выше.

Детерминированность и границы утилиты

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

Хорошие кандидаты в утилиту: скидка, тариф, комиссия, классификатор, лимит, нормализатор значения, command decision («разрешить» / «отклонить» по понятным числовым порогам). Плохие кандидаты: «отправить письмо», «получить курс валют из сети», «записать заказ в транзакции». Как только вычисление зависит от внешнего мира или производит эффект, оно перестаёт быть воспроизводимым — и должно остаться в обычном коде, который вызывает утилиту, а не наоборот.

Практика в песочнице

  1. Откройте вкладку «Выполнить» и посчитайте скидку для суммы 12 000 без статуса постоянного клиента. Затем добавьте статус — убедитесь, что результат вырос ровно на 5 % от той же суммы, а не пересчитался с нуля.
  2. Откройте delivery-price.fts (вкладка check) и найдите два правила, которые вносят базовый тариф: нижнюю границу держат они, а не строка начинает с — та задаёт ноль. Определите, какое ключевое слово в свойстве отвечает за «не меньше», а не за «не больше».
  3. В order-discount.fts замените потолок 15000 на более жёсткий — например, результат не больше 10 процентов от поля сумма — и запустите run для постоянного клиента с суммой 20 000. Скидка 3000 против предела 2000: убедитесь, что вкладка показывает диагностику, а не подправленное число.

Типичные ошибки

  • FTS_EXPECTED_KEYWORD — файл не начинается с категория «Имя». Естественный разбор требует объявление категории первой строкой; без него компилятор откатывается к устаревшему скобочному синтаксису и жалуется на отсутствие буквального category. Проверьте первую непустую строку файла.
  • FTS_NATURAL_DECLARATION — на верхнем уровне категории встречено слово, которое не является объект, структура, морфизм, теорема или утилита (например, опечатка вроде утилитта или собственное слово штука). Компилятор называет допустимый список прямо в сообщении.
  • FTS_UTILITY_PROPERTY — правила отработали, но результат не прошёл сравнение в свойстве. Это не ошибка синтаксиса, а сигнал о том, что входные данные или сами правила выводят результат за декларированную границу. Сообщение называет и свойство, и утилиту: нарушено свойство «Имя» утилиты «Имя».

Чек-лист

  • У утилиты есть принимает, возвращает и начинает с — без начального значения утилита не компилируется.
  • Каждое правило начинается с если и заканчивается одним действием: то добавить или то результат равен.
  • Несколько условий одного правила — отдельные строки и, а не одна строка через запятую.
  • Порядковые сравнения (больше, меньше, не больше, не меньше) — только для чисел; для остального есть равен и не равен.
  • Свойство — одно сравнение результата, которое проверяется после всех правил и не пытается ничего исправить сам.
  • Хотя бы один пример у утилиты обязателен для fts test; без примеров команда завершится с FTS_NO_UTILITY_EXAMPLES — подробнее в следующей главе.

Кейсы каталога по этой теме

Дальше: примеры как тесты

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов
Дальше