Материал Executable FTS utilities: rules, order, and properties
0%

Executable FTS utilities: rules, order, and properties

FTS Executable Utilities: Rules, Order, and Properties

A utility is a pure deterministic computation over one object: tariff, discount, classifier, limit. It does not access the network and does not write to the database. In this chapter — how to write several rules so that the order is predictable, not guessed based on code reading experience, and how to make the compiler stop if the result goes beyond acceptable boundaries.

Minimal example

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

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

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

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

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

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

The object describes the input form. The utility names the input (принимает), the result type (возвращает) and the required initial value (начинает с). Without it, the result does not emerge from the implicit null or 0 by default runtime — zero in the example above is written by the model author, not inserted by the compiler.

What the compiler does

  1. Natural syntax is parsed into canonical FtsDocument — the same format is read by validate, the interpreter, and the TypeScript generator.
  2. validate checks the structure: each rule has a condition and an action, each property has one result comparison, and references to fields exist in the принимает object.
  3. Upon execution (executeUtility or the generated function), the result receives начинает с.
  4. Rules are processed from top to bottom; for each, the condition is checked.
  5. For rules with a true condition, the action is executed: то результат равен replaces the result, то добавить adds a number to it.
  6. After all rules are processed in order, all properties are checked.
  7. If a property is violated, execution stops with diagnostic FTS_UTILITY_PROPERTY — the value does not escape.
  8. If all properties are satisfied, the calling side receives the final result.

This sequence is the same for the interpreter and for the generated fts.utilities.ts: the generator has no right to optimize the rule order or skip a property check.

Rules are not if/else

All rules whose conditions are true are executed in the order of declaration. This is a fundamental difference from the if / else if chain, where a triggered branch excludes the others:

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

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

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

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

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

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

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

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

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

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

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

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

For the example with twenty thousand, three out of four rules apply: 10% + 5% + 7% of 20 000 give 4 400. No rule “cancels” another — they add up, because each of them adds to the overall result.

The discount ceiling is recorded as a number (результат не больше 22000), not as a percentage of the field, and the boundaries by amount are stated directly in the rule conditions. The property is checked across the entire input domain, not just where the rules triggered: a limit of the form 25 процентов от поля сумма would go negative on a negative sum and break the calculation where no rule triggered. A detailed walkthrough is in the chapter on antipatterns.

Several conditions of one rule are joined by the word и: each subsequent condition — a separate line with the same indentation as если. The third rule will trigger only if both lines are true simultaneously.

The order is not meaningless: if a rule uses то результат равен, it overwrites everything accumulated by the previous rules. Such a rule should be declared last consciously, and not rely on the compiler to assign priorities on its own — it doesn’t do that.

Comparisons and Percentages

Six comparisons are available: равен, не равен, больше, меньше, не больше, не меньше. равен and не равен work for any scalar value — number, flag, string. The remaining four require both sides of the comparison to be numbers: they will not compare a string with a date, the compiler will not allow such a utility through validate, and at runtime comparing incompatible types stops execution.

N процентов от поля X is computed from the value of the X field in the input data, not from the current result. The field name is specified exactly as it is declared in the object, without any grammatical cases: 10 процентов от поля сумма, 2 процента от поля «страховая сумма». The compiler does not attempt to decline Russian nouns — this is an intentional limitation, not an oversight.

Property — postcondition, not autocorrection

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

The property is checked after all rules have executed. If the comparison is false, FTS does not trim the discount to an acceptable limit and does not choose a “safe” number instead of the incorrect one — it halts execution with diagnostic FTS_UTILITY_PROPERTY and the name of the violated property. This is a deliberate boundary: silent correction of the result would hide the error in the model or in the input data, while rolling back outward forces it to be seen and fixed in the rule itself.

A property is written as one comparison of the result — результат <сравнение> <выражение>. An expression can be a number, a field value, or a percentage of a field, as in the example above.

Determinism and Utility Bounds

The same input data always produce the same result: the utility does not read the clock, does not call outside, and does not store state between invocations. This allows testing it without mocks and reusing the same calculation in the interpreter, in generated TypeScript, and in the browser sandbox — they must match because they read the same model.

Good candidates for utility: discount, tariff, commission, classifier, limit, value normalizer, command decision (“allow”/“reject” based on clear numeric thresholds). Bad candidates: “send an email”, “get currency rate from the network”, “record order in transactions”. As soon as a computation depends on the outside world or produces an effect, it ceases to be reproducible — and should remain in regular code that calls the utility, and not the other way around.

Practice in the sandbox

  1. Open the “Execute” tab and calculate the discount for the amount 12 000 without the regular customer status. Then add the status — make sure the result increases exactly by 5 % of the same amount, not recalculated from zero.
  2. Open delivery-price.fts (tab check) and find two rules that apply the base rate: they set the lower limit, not the line начинает с — that one sets zero. Determine which keyword in the property is responsible for “not less than”, not for “not more than”.
  3. In order-discount.fts replace the ceiling 15000 with a stricter one — for example, результат не больше 10 процентов от поля сумма — and run run for a regular customer with the amount 20 000. A discount of 3000 against the limit of 2000: make sure the tab shows diagnostics, not the corrected number.

Common Fallacies

  • FTS_EXPECTED_KEYWORD — file does not start with категория «Имя». Natural parsing requires the category declaration as the first line; without it the compiler reverts to the outdated bracket syntax and complains about the absence of a literal category. Check the first non-empty line of the file.
  • FTS_NATURAL_DECLARATION — at the top level of the category, a word was encountered that is not объект, структура, морфизм, теорема, or утилита (for example, a typo like утилитта or a custom word штука). The compiler lists the valid options directly in the message.
  • FTS_UTILITY_PROPERTY — the rules executed, but the result failed the property check. This is not a syntax error, but a signal that the input data or the rules themselves produce a result beyond the declared boundary. The message names both the property and the utility: нарушено свойство «Имя» утилиты «Имя».

Checklist

  • The utility has принимает, возвращает and начинает с — without an initial value the utility won’t compile.
  • Each rule starts with если and ends with one action: то добавить or то результат равен.
  • Multiple conditions of one rule — separate lines и, not one line separated by commas.
  • Ordinal comparisons (больше, меньше, не больше, не меньше) — only for numbers; for the rest there are равен and не равен.
  • A property — one result comparison, checked after all rules and not trying to fix anything itself.
  • At least one example is required for fts test; without examples the command will end with FTS_NO_UTILITY_EXAMPLES — more details in the next chapter.

Cases in the catalog on this topic

Next: examples as tests

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

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

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

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