* Add community knowledge on AL boolean operators not short-circuiting AL gives no short-circuit (lazy) evaluation guarantee for and/or/xor — neither the AL operators nor the boolean operators documentation defines a lazy evaluation order. LLMs trained on C#, JavaScript, or SQL assume the left operand guards the right, which produces conditions where a guard does not protect an unsafe subscript or a field read after a failed Get, and where an expensive operand is paid on every path. Adds community/knowledge/performance/boolean-operators-do-not-short-circuit.md with good/bad AL companions. The guidance prefers nested if when one operand depends on another, while keeping and/or legitimate for operands that are independently safe and cheap, so a reviewer does not flag harmless bound checks. This is the first article in a community performance domain; the Microsoft performance review leaf skill already sources candidates by domain across every enabled layer, so no skill change is needed to reach it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Add case true of pattern for long condition chains Follow-up from PR review: nested if is the right answer for two or three dependent conditions, but past that the nesting becomes the problem. AL's case statement is the flat alternative — the control statements documentation states a value set "must be an expression or a range" and that the first matching value set executes, so case true of / case false of accept boolean expressions and stop at the first match. That is the laziness the boolean operators do not provide. Adds case-true-of-for-long-condition-chains.md with good/bad AL companions: case false of for guard chains where every condition must hold, case true of for first-match dispatch. The bad sample shows both failure shapes — a five-level if ladder, and the worse escape of collapsing it into an and chain, which trades nesting for a real defect. Cross-links both articles, and adds the threshold to the short-circuit article's Best Practice so following it does not lead to a deep ladder. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Comma-group the pure value sets in the case false of sample Review feedback: the five value sets sharing exit(false) should be comma separated. Applied to the three that are pure field reads with no order dependency. The Get and the Blocked read keep their own value sets. The documentation guarantees that the first matching value set executes, which orders matching across separate value sets; it says nothing about evaluation within one comma-separated set, and the natural lowering of that is an equality-or chain — where AL's or does not short-circuit. Grouping the Get with the checks that must precede it would rest the sample's correctness on undocumented behaviour, which is the defect these two articles exist to prevent. Encodes the boundary in the Best Practice section so the grouping is applied where it is safe and not where it is not, and scopes the repeated-exit detection signal explicitly to nested chains so the case sample does not read as its own anti-pattern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Use a single exit(false) for the whole case false of chain Review decision: all five conditions share one action, so they share one comma-separated value set with a single exit(false). Aligns the article's Best Practice with the sample — it previously told authors to keep side-effecting conditions in their own value set, which the sample no longer does — and drops the now-contradictory wording about listing the failure action per condition. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Drop the parentheses from the case false of value sets Applies NKarolak's review suggestion: a case value set needs no parentheses around a comparison. Encodes the rationale in the Best Practice section, since it is a real advantage of the pattern and is not documented elsewhere in the repo. The AL operator hierarchy places and/xor above the comparison operators and or just above them too, so parentheses are mandatory in an and chain — A = B and C = D misparses without them — while a case value set has no and to bind tighter and needs none. That inverts the precedence most developers arrive with from C#. Also notes in the bad sample that its parentheses are not optional, so the two samples contrast on parentheses as well as on evaluation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Address correctness review: or/xor pattern, case value-set ordering Fixes two points from JesperSchulz's review on PR #136. 1. boolean-operators-do-not-short-circuit.md gave one fix — nested if — for and, or, and xor alike. That's only correct for and: nesting if A then if B then Action drops the A-true/B-false case of A or B, silently changing the result. Gives or its own early-exit pattern (if A then exit(true); exit(B)), warns explicitly against applying the and-rewrite to or, and clarifies that xor is not a short-circuit candidate in any language since its result always depends on both operands. Adds an or fixture (IsEligibleForFreeShipping) to both samples so an agent has a concrete pattern to match instead of extrapolating from the and-only examples. 2. case-true-of-for-long-condition-chains.md derived stop-at-first-match for one comma-separated value set from the documentation's guarantee about the first matching value set — plural, i.e. ordering across value sets, which is not the same claim. The good sample's Item.Get / Blocked pair depended on the one the docs don't make. Restructures the sample to only comma-group the three pure, order-independent checks; Get and Blocked keep their own value sets, in order, relying solely on the guarantee that is actually documented. Description and Anti Pattern now state that boundary so it isn't re-collapsed later. Also carries forward a parenthesis fix (not Item.Blocked in the collapsed bad sample) that was made two commits ago but never landed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
4 KiB
| bc-version | domain | keywords | technologies | countries | application-area | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
performance |
|
|
|
|
Use case true of for long chains of dependent conditions
Description
Because AL gives no short-circuit guarantee for and and or, a chain of conditions that must be evaluated in order has to be sequenced with nested if statements — and past three conditions the nesting itself becomes the problem: the body drifts right, the order of evaluation is carried by indentation alone, and any shared failure path is repeated at every level. AL's case statement is the flat alternative. Its value sets "must be an expression or a range", so case true of and case false of accept arbitrary boolean expressions, and the statement "is evaluated, and the first matching value set executes the associated statement" — evaluation stops at the first matching value set, which is exactly the laziness the boolean operators do not provide. That guarantee is stated for value sets, plural: it orders evaluation across separate value sets, and says nothing about the order of the individual expressions listed inside one comma-separated value set.
Best Practice
Sequence two or three dependent conditions with nested if. Beyond that, switch to case: use case false of for a chain of guards where every condition must hold, letting control fall past end when all of them pass; use case true of for first-match dispatch, where each later probe runs only if the earlier ones did not match. Comma-separate conditions into one value set only when every one of them is a pure, order-independent test with no side effect — a field comparison, an enum check, a bound test — so it makes no difference whether AL evaluates all of them or stops early; grouping these costs nothing and removes the repeated action. A condition that guards another, or that carries a side effect or a cost of its own — a Get, a Find, a procedure call — keeps its own value set, placed immediately after the value set it depends on, so the code relies only on the ordering the documentation actually states. A value set needs no parentheses around a comparison, unlike an operand of and or or: the AL operator hierarchy places and and or above the comparison operators, so parentheses are mandatory there and the chain fills up with them. This keeps every condition at one indentation level, makes evaluation order explicit rather than implied by nesting, and preserves the stop-at-first-match behaviour it relies on. It also aligns with the AL programming convention that more than two alternatives belong in a case statement rather than an if-then-else.
See sample: case-true-of-for-long-condition-chains.good.al.
Anti Pattern
An if ladder four or more levels deep whose only purpose is sequencing guards. Detection: a chain of nested if statements with no else, each condition guarding the one below it, terminating in a single action or exit; or the same exit/error duplicated at every level of such a nested chain, purely to escape it. The second, worse form is collapsing that ladder into one and chain to escape the nesting — that trades indentation for a real defect, because the operands are still all evaluated. A third, subtler form is over-applying the comma-grouping itself: putting a guard and the condition it protects — for example Item.Get(...) and a read of a field on that same record — into one comma-separated value set. That relies on an evaluation order within a single value set that the documentation does not state; keep them in separate value sets instead. Reach for case over nested if or a collapsed and chain, and keep order-dependent conditions in their own value sets within it.
See sample: case-true-of-for-long-condition-chains.bad.al.
See also
boolean-operators-do-not-short-circuit.md covers the underlying evaluation rule that makes the sequencing necessary in the first place.