If I have a String variable that might be unset, how can I conditionally assign a default value to it only when it has not been set yet?
← Operators and Expressions · Ref: Q1263
EK9 has a dedicated operator for 'assign if unset': :=? (assign-if-unset). It assigns the right-hand value to the variable ONLY if the variable is currently unset. If the variable already has a value, the assignment is skipped - the existing value is preserved.
THE OPERATOR
variable :=? defaultValue
WORKED EXAMPLE - SOME PATHS SET IT, ONE DOES NOT
greetingFor()
-> hour as Integer
<- rtn as String: String() // unset until a branch sets it
if hour < NOON
rtn := "Good morning"
else if hour < EVENING
rtn := "Good afternoon"
rtn :=? "Hello, World" // the evening falls through unset: only then is the default used
greetingFor(9) // "Good morning" - :=? skipped greetingFor(14) // "Good afternoon" - :=? skipped greetingFor(21) // "Hello, World" - :=? assigned
After the if / else if, rtn MIGHT be set: two paths set it and the fall-through does not. That is what :=? is for - it decides at run time, so the default never overwrites a greeting a branch chose.
A VALUE THAT IS ALREADY SET IS KEPT
loadStoredTitle()
<- rtn as String: "Untitled"
title <- loadStoredTitle() // already set: "Untitled" title :=? "Default Title" // SKIPPED, because title is set stdout.println(title) // still "Untitled"
WHEN THE COMPILER ALREADY KNOWS THE ANSWER
:=? is for a value that MAY be unset. Where the compiler can see the outcome it says so:
- straight after 'greeting as String: String()' the target is always unset, so ':=?' always applies - a plain assignment written as a conditional one (E08097). Write greeting := "Hello, World", or declare it with the value: greeting as String: "Hello, World".
- on a target it can see is set (title <- "Untitled", from a literal), it can never apply (E08095).
A variable declared with no value at all ('greeting as String?') is the exception: filling that empty slot is what :=? is for, so neither error applies.
WHY NOT IF + ASSIGNMENT?
The long-form equivalent works but is more verbose:
if not rtn? rtn := "Hello, World"
The :=? operator collapses this into one line. It is also IMPOSSIBLE to forget the negation - there is only one direction the operator can go.
NOT TO BE CONFUSED WITH OTHER OPERATORS
- := is plain assignment (always assigns, overwrites)
- :=? is assign-if-unset (assigns only if unset)
- <- is declaration + first assignment (creates a new variable)
- <? is coalescing minimum (RETURNS the smaller value, completely different family)
See Q1264 for Integer assign-if-unset. See Q1265 for Date. See Q1266 for custom record. See Q1226 for the contrast between :=? and <?.
Example
#!ek9 defines module qa.operators.guardedassignstring defines constant NOON <- 12 EVENING <- 18 defines function //Two paths set the greeting and the evening falls through without one: only at run time is it known //whether rtn is set, so ':=?' really decides - the default is used for the evening alone. greetingFor() -> hour as Integer <- rtn as String: String() if hour < NOON rtn := "Good morning" else if hour < EVENING rtn := "Good afternoon" rtn :=? "Hello, World" //Sourced rather than a literal, so 'title' is genuinely already-set at runtime without //the compiler being able to prove it and reject the ':=?' below as dead (E08095). loadStoredTitle() <- rtn as String: "Untitled" defines program GuardedAssignStringDemo() stdout <- Stdout() stdout.println(`morning :=? : ${greetingFor(9)}`) stdout.println(`afternoon :=? : ${greetingFor(14)}`) stdout.println(`evening :=? : ${greetingFor(21)}`) //Already set when it arrives: ':=?' keeps it title <- loadStoredTitle() title :=? "Default Title" stdout.println(`pre-set :=? : ${title}`)
Common mistakes
E08097 — String() makes an unset String, so the ':=?' after it always applies - a plain ':=' written as a conditional one. Declare the variable with the value, or keep ':=?' for a value that MAY be unset (from a call, or after a branch that may not set it). See ek9 -h E08097 for details.
Incorrect:
title <- String()
Correct:
title <- loadStoredTitle()
E08095 — :=? only assigns when the target is unset. A title declared from a literal is always set, so the compiler rejects the assign-if-unset as one that can never apply (E08095) - and from String() it would always apply (E08097). Take it from where it may or may not be set. See ek9 -h E08095 for details.
Incorrect:
title <- "Untitled"
Correct:
title <- loadStoredTitle()
Other ways to ask this
- How do I assign a default to a String only when it is unset?
- Show me the EK9 way to provide a fallback value for an unset String.
- What is the operator that assigns only when the target is unset?
- Give me a worked example of conditional default assignment on a String.
Coming from another language?
Java: if (variable == null) variable = default. Kotlin: variable = variable ?: default. Python: variable = variable or default (works but conflates falsy with None). Rust: variable = variable.unwrap_or(default). JavaScript: variable = variable ?? default (nullish coalescing assignment). EK9: variable :=? default — direct, single operator, only assigns when target is unset.
Keywords: :=?, String, assign-if-unset, conditional assignment, default value, fallback, unset