How does the ?= guarded assignment protect against unset values in control flow?
← Control Flow · Ref: Q1024
The ?= operator ALWAYS evaluates the right side expression first. It then checks if the result is SET (not null and isSet returns true). Only if the result IS set does it assign to the target variable and proceed into the control flow body.
IN IF:
if data ?= fetchRecord(id) process(data)
fetchRecord is called. If the result is set, data is assigned and the body executes. If unset, the body is skipped entirely.
IN SWITCH:
switch value ?= parseInput(text) case 1 ...
parseInput is called. If result is unset, the entire switch (including default) is skipped. No bad data is assigned.
KEY INSIGHT: ?= protects the TARGET variable from receiving unset data. If the expression returns something unset, the target stays unchanged. This is critical for:
- Network calls that might fail - Database lookups that might find nothing - Parsing that might produce invalid results
COMPARISON:
:= always assigns (no protection) ?= checks result BEFORE assigning (protects against bad data) :=? checks target BEFORE evaluating (lazy, avoids unnecessary work)
See Q1023 for all three operators compared. See Q759 for ?= returning Boolean. See Q74 for <- declaration guard.
Example
defines module qa.controlflow.guarded.assignment.flow defines function fetchSetValue() <- rtn as Integer? rtn: 42 fetchUnsetValue() <- rtn as Integer? rtn: Integer() defines program GuardedAssignmentFlowDemo() stdout <- Stdout() // === ?= IN IF: protects against unset === existing <- Integer() if existing ?= fetchSetValue() stdout.println("IF ?= with set value: assigned " + $existing) // Reset for next demo existing: Integer() if existing ?= fetchUnsetValue() stdout.println("This should not print") else stdout.println("IF ?= with unset value: body skipped") // === ?= IN SWITCH: skips entire switch when unset === switchControl <- Integer() switch switchControl ?= fetchUnsetValue() case 42 stdout.println("Switch matched 42") default stdout.println("Switch hit default") if switchControl? stdout.println("Switch entered with value") else stdout.println("Switch skipped entirely (RHS unset)") // === ?= IN SWITCH: enters switch when set === switchValid <- Integer() switch switchValid ?= fetchSetValue() case 42 stdout.println("Switch matched 42 from set value") default stdout.println("Switch hit default from set value")
Common mistakes
E01073 — 'null' does not exist in EK9 - use '?=' to evaluate, isSet-check and conditionally assign in one step. See ek9 -h E01073 for details.
Incorrect:
if existing != null
Correct:
if existing ?= fetchSetValue()
Other ways to ask this
- How does ?= work in if and switch statements?
- What happens when ?= gets an unset value in EK9?
- How do I safely assign from a function that might return unset?
- Show me ?= guarded assignment in switch and if in EK9
Coming from another language?
Java: Must write 'var temp = fetch(); if (temp != null && temp.isValid()) { data = temp; switch(data) {...} }'. EK9: 'switch data ?= fetch()' does all of this in one line. Rust: if let Some(v) = expr — similar concept but no assignment to existing variable. Go: No equivalent — must check err separately.
Keywords: assignment, control, flow, protect, guarded, switch, null-safe, if, validate, guard, safe, check, isset, unset