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>
This commit is contained in:
António Silva 2026-08-25 12:05:29 +01:00
parent 63bd69d7e5
commit f7a00bf0b6
2 changed files with 12 additions and 8 deletions

View file

@ -4,16 +4,20 @@ codeunit 50542 "Perf Sample CaseChain Good"
var
Item: Record Item;
begin
// 'case false of' evaluates the value sets in order and stops at the first
// match, so each condition is reached only when the previous one passed —
// 'case false of' matches the value sets in order and stops at the first
// match, so each value set is reached only when the previous one passed —
// and Item.Get is never called for a non-item line.
case false of
(SalesLine.Type = SalesLine.Type::Item):
exit(false);
(SalesLine."No." <> ''):
exit(false);
// Pure field reads with no side effects and no order dependency, so
// they share one value set and one action.
(SalesLine.Type = SalesLine.Type::Item),
(SalesLine."No." <> ''),
(SalesLine."Qty. to Ship" > 0):
exit(false);
// These two keep their own value sets even though the action repeats:
// ordering across separate value sets is what the documentation
// guarantees. The Get must not run until the checks above pass, and
// Blocked must not be read until the Get succeeded.
Item.Get(SalesLine."No."):
exit(false);
(not Item.Blocked):

View file

@ -15,13 +15,13 @@ Because AL gives no short-circuit guarantee for `and` and `or`, a chain of condi
## 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, listing the failure action per condition and letting control fall past `end` when all pass; use `case true of` for first-match dispatch, where each later probe runs only if the earlier ones did not match. 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 also aligns with the AL programming convention that more than two alternatives belong in a `case` statement rather than an `if-then-else`.
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, listing the failure action per condition and letting control fall past `end` when all pass; use `case true of` for first-match dispatch, where each later probe runs only if the earlier ones did not match. Value sets that share an action may be comma-separated into one value set, but only when each is a pure, order-independent test — a field comparison or an enum check. A condition with a side effect, or one that is only safe after an earlier condition passed, keeps its own value set even when its action repeats, because ordered matching is guaranteed *across* value sets, not within one. 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 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 chain. 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. Reach for `case` instead of either.
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. Reach for `case` instead of either.
See sample: `case-true-of-for-long-condition-chains.bad.al`.