What is the difference between :=, :=? and the explicit re-point-and-check in EK9 control flow?
← Control Flow · Ref: Q1023
EK9 has two assignment pre-flows for EXISTING variables, and one explicit pattern for re-pointing a variable and entering only when the new value is set.
:= (BLIND ASSIGNMENT)
Always evaluates the expression. Always assigns, an unset result included. No checks: the body always runs.
switch mainValue := getValue() case 1 ...
Use when: the value is known safe, or a 'with'/'then' condition validates it.
:=? (ASSIGN-IF-UNSET - checks the LEFT side)
First checks whether the target is already set. Only evaluates and assigns the expression if the target is UNSET; a target that is already set keeps its value. The body then runs only if the target is SET.
switch mainValue :=? getValue() case 1 ...
Use when: the variable may already have a value (caching, defaults, priority chains). Avoids re-evaluation.
RE-POINT, THEN CHECK (assign, then run only if the new value is set)
Assign with ':=' (an unset result OVERWRITES the old value), then check it. In an if or while the check fits in the head: 'if mainValue := getValue() then mainValue?'. A switch, for, try or do head has no condition to carry it, so write the three steps:
mainValue := getValue() if mainValue? switch mainValue case 1 ...
DECISION TREE:
First time using this variable? -> Use <- (declaration guard) Variable exists, keep it if already set? -> Use :=? (lazy, avoids re-evaluation) Variable exists, take the new value and enter only if it is set? -> ':=' then check it ('then x?', or 'if x?' around the construct) Variable exists, value known safe? -> Use := (blind, no checks)
See Q74 for the declaration guard (<-). See Q79 for :=? standalone usage. See Q759 for assigning and then checking. See Q1024 for re-point-and-check across control flow. See Q1025 for := vs :=? across control flow.
Example
defines module qa.controlflow.guard.operator.contrast defines function getZero() <- rtn as Integer? rtn: 0 getUnsetValue() <- rtn as Integer? rtn: Integer() getOne() <- rtn as Integer? rtn: 1 defines program GuardOperatorContrastDemo() stdout <- Stdout() // === := BLIND ASSIGNMENT === // Always assigns. getZero() returns 0, matches no case -> default. blindResult <- Integer() switch blindResult := getZero() case 1 stdout.println(":= matched case 1") default stdout.println(`:= hit default (value: ${blindResult})`) // === RE-POINT, THEN CHECK (assign, then switch only if the new value is set) === // guardedResult starts SET (1). getUnsetValue() returns an unset Integer. // ':=' still ASSIGNS it, so guardedResult becomes unset and the 'if' skips the switch. guardedResult <- getOne() guardedResult := getUnsetValue() if guardedResult? switch guardedResult case 1 stdout.println("re-pointed matched case 1") default stdout.println("re-pointed hit default") stdout.println(`re-point kept a value (value: ${guardedResult})`) else stdout.println("re-point assigned the unset result, so the switch was skipped") // === :=? ASSIGNMENT IF UNSET (checks LEFT side) === // lazyResult starts unset, so :=? WILL evaluate getOne(). // getOne() returns 1, which is set, so assignment happens and matches case 1. lazyResult <- Integer() switch lazyResult :=? getOne() case 1 stdout.println(`:=? assigned (value: ${lazyResult})`) default stdout.println(":=? hit default") // lazyResult is now SET, so :=? does not call getZero() and keeps the 1. switch lazyResult :=? getZero() case 1 stdout.println(`:=? kept the existing value (value: ${lazyResult})`) default stdout.println(`:=? replaced it (value: ${lazyResult})`)
Common mistakes
E01073 — EK9 has no null; use the tri-state '?' isSet operator (or a guard) instead of comparing to null. See ek9 -h E01073 for details.
Incorrect:
if guardedResult <> null
Correct:
if guardedResult?
Other ways to ask this
- When should I use := vs :=? in a switch statement?
- How do the assignment pre-flows differ in EK9?
- What guard operator should I use in EK9 control flow?
- How do I re-point a variable and skip a switch when the new value is unset?
Coming from another language?
Java: No equivalent. Must manually write if/else chains for each pattern. Optional.orElse() handles one level. Kotlin: Elvis (?:) handles null-only cases, not tri-state isSet. Rust: if let handles pattern matching, no assignment-if-unset. Go: if err := f(); err != nil — closest to := but no :=? equivalent. EK9: blind assignment (:=), lazy assign-if-unset (:=?) and the declaration guard (<-), plus the explicit 'x := f() then x?' to re-point and check — one syntax across all control flow.
Keywords: lazy, decision, operator, contrast, blind, switch, control, comparison, unset, guard, guarded, assignment, difference, choose, flow