How do I run a block only when an EXISTING variable gets a set value?
← Control Flow · Ref: Q1962
Assign it with ':=' and check the new value in the condition: 'if reading := fetchReading() then reading?'. ':=' always assigns - an unset value included, overwriting what reading held - and checks nothing, so the check is written out after 'then'. The body runs only when reading is now set. (EK9 has no '?=' guard; this explicit form replaced it.)
if reading := fetchReading() then reading? // runs only when reading is now set
A ':=' head with no condition is E07340: an assignment pre-flow checks nothing, so it needs 'then' (or 'with') and a condition.
WITH A FURTHER CONDITION
Put the set-check first and PARENTHESISE the rest: 'then sample? and (taken < wanted)'. 'and', 'or' and 'xor' share one precedence level, so 'sample? and a or b' would read as '(sample? and a) or b' and could enter with sample unset.
IN A LOOP
The pre-flow runs ONCE, before the loop; the condition (set-check included) is tested on every pass:
while sample := firstSample() then sample? and (taken < wanted)
SWITCH, FOR, TRY AND DO
Their heads have no condition to carry the check, so assign first and wrap the construct: 'reading := fetchReading()' then 'if reading?' around the switch. If the variable is only used inside, a declaration guard does it in one head: 'switch r <- fetchReading()'.
See Q1960 for re-pointing inside a safe access block (E08040), Q1963 for why a ':=' head is not a guard and Q74 for guard variables in if.
Example
defines module qa.flow.guard.declarationoverguard defines function fetchReading() <- rtn as Integer: 21 fetchNoReading() <- rtn as Integer: Integer() firstSample() <- rtn as Integer: 10 defines program HeadGuardDemo() stdout <- Stdout() reading <- 0 stdout.println(`before: reading=${reading}`) if reading := fetchReading() then reading? stdout.println(`guarded body: reading=${reading}`) stdout.println(`after a set value: reading set=${reading?}`) bodyRan <- false if reading := fetchNoReading() then reading? bodyRan: true stdout.println(`after an unset value: body ran=${bodyRan} reading set=${reading?}`) sample <- 0 taken <- 0 wanted <- 2 while sample := firstSample() then sample? and (taken < wanted) stdout.println(`sample ${sample} taken ${taken}`) taken++ stdout.println("Done")
Common mistakes
E07340 — A ':=' head assigns and checks nothing, so on its own it decides nothing; give it a condition - the set-check of the new value. See ek9 -h E07340 for details.
Incorrect:
if reading := fetchReading()
Correct:
if reading := fetchReading() then reading?
Other ways to ask this
- Why does 'if reading := fetchReading()' need a 'then'?
- How do I re-point a variable in an if head and check the new value?
- How do I guard a block on an existing variable getting a value?
- Why does a while loop re-check the variable on every pass?
- What replaced the ?= guard in EK9?
Coming from another language?
Swift: 'if let v = f()' binds and tests in one step, but only a NEW name. Kotlin: 'x = f(); if (x != null)'. EK9: 'if x := f() then x?' re-points the existing variable and tests it in one head; for a new name, 'if v <- f()' declares and tests.
Keywords: then, :=, re-point, if head, existing variable, guard, pre-flow, set check, E07340