Expressions and quantities: pitfalls
Wrong forms, silent outcomes, LDC-E diagnostics, typical mistakes of
formalizers and AI agents, how to detect them.
1. Sum smaller than expected on equal summands
Section titled “1. Sum smaller than expected on equal summands”sum(collect a: Money …) counts distinct values (Set); two equal
incomes are one element, and the sum silently undercounts: no refusal, no
warning. Write collect all a: Money, m: Employee where … — the second
variable tells apart substitutions with equal values. Statics
cannot see the difference: guard it with a test on two equal facts. The
research.expressions.counted_threshold example deliberately gives three members equal
50 000 KZT payouts — the summary must be 150 000, not 50 000.
2. Generator name unbound
Section titled “2. Generator name unbound”LDC-E1317: in collect all a: Money where household_member(h, m) …
the variable m comes from nowhere — declare it as a second comprehension
binder (collect all a: Money, m: Employee where …).
3. Threshold of the wrong kind
Section titled “3. Threshold of the wrong kind”n >= 10.5 with n: Integer, Integer + Rational, Rational + Decimal,
Date == Instant — LDC-E2108. The threshold in the fact’s kind; take a share
one way for the whole computation.
4. Money divisor — a whole number
Section titled “4. Money divisor — a whole number”Money / Integer, Integer / Decimal — LDC-E2108: a money divisor is
Decimal (12.0, not 12); same for div_round. A variable divisor
n: Integer is lifted by multiplying n * 1.0, not by a separate “share” fact:
feeding a share as a case fact takes the act’s prescribed computation out of the
act.
5. Rounding without a policy
Section titled “5. Rounding without a policy”round(x) without precision/mode — LDC-E2116: both arguments mandatory,
mode — the "HALF_UP" string. Type Percentage — LDC-E2101:
a percent constant is declared Decimal (const BONUS_RATE: Decimal = 25 percent), because 25 percent is the exact Decimal 0.25.
6. Silent execution refusals instead of statics
Section titled “6. Silent execution refusals instead of statics”g < Senior, 500 kg > 100 m, 1000 KZT + 5 USD — check stays silent, the
refusal comes at execution (TYPE_ERROR, CURRENCY_MISMATCH): the rule does not
fire, the case carries an issue. Guard with a scenario test: a positive test
shows the norm fires, a negative one shows the silence is exactly
NEITHER, not a refusal.
7. Sum over an empty list is not zero
Section titled “7. Sum over an empty list is not zero”sum over an empty List is EMPTY_AGGREGATE, not 0 KZT: an empty sum has
no currency, and the core does not invent one. Three resolutions: a norm-side
policy — sum(collect all …, empty: 0 KZT); an explicit aloud
refusal — empty: error (indistinguishable in CLIR from no policy); the zero
case by a second rule with not_known or closure. The policy kind must match
the element kind: empty: 0 over a Money collection — LDC-E2119, not
“zero in tenge”. empty on count and precision/rounding, not on
average — LDC-E2120.
8. Division with rounding in two steps
Section titled “8. Division with rounding in two steps”round(base * div_round(a, b, 6, …), 2, …) is double rounding, diverging
from the norm: rounding the share separately and then the total is a mistake, not
caution. The rounding is one — over the exact result (div_round(base * (a / 1 KZT), b / 1 KZT, p, mode)). A decimal share approximation (s * 0.6667) is the same mistake one class down: 30 000 × 0.6667 = 20 001, not
20 000.
9. Bare domain constant instead of a declaration
Section titled “9. Bare domain constant instead of a declaration”A name in term position resolving to no declaration is an unresolved
name, static refusal LDC-E1330: a “bare
domain constant” is not a term form. An individual is named only by
declaration: const KZ: Jurisdiction = entity_ref("…") — a constructor
gives the value, const gives the name. AI agents love writing jurisdiction and
currency names straight into terms — caught only by this check.
10. Cyclic constant
Section titled “10. Cyclic constant”A constant through itself is a static error, not a value:
CYCLIC_CONSTANT before any conclusion. A constant through a constant is legal
and unfolds recursively; a cycle is not.
11. AI-agent traps
Section titled “11. AI-agent traps”- Writing
Money / Integer(“sum over a month count”) instead ofdiv_round(s, n * 1.0, p, mode)— item 4. - Declaring a percent with type
Percentageinstead ofDecimal— item 5. - Counting
sum(collect …)as a charge summary instead ofcollect all— item 1. - Converting units by division (
x / 3.6) instead ofconvert(value, num, den, unit); attaching a tag by multiplication instead ofwith_unit. - Expecting
round(s * (2 / 3), …)to save the total: the product fails withINEXACT_DIVISIONbeforeroundever sees it — the norm’s exact fraction needsdiv_round(s * 2, 3.0, p, mode).
12. Off-grammar pattern and division by zero (from page 08-expressions)
Section titled “12. Off-grammar pattern and division by zero (from page 08-expressions)”text_matches(t, …)with other than two arguments —LDC-E2103; a literal pattern outside the pattern grammar —LDC-E2117./andmodby zero —DIVISION_BY_ZERO: an execution refusal, not a static one — the rule does not fire, the case carries the issue.- Inexact money and quantity quotients without a policy (
100 KZT / 3.0,100 KZT * (1 / 3)) —INEXACT_DIVISION: divide underdiv_roundwith an explicit policy (see item 8).
Documentation for Arxo. Writings — blog.arxo.io.
Anonymous visit counts on stats.arxo.io, no cookies.