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': :=? (guarded assignment). 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 silently skipped — the existing value is preserved.

THE OPERATOR

  variable :=? defaultValue

WORKED EXAMPLE

  greeting as String: String()       // unset
  greeting :=? "Hello, World"        // assigned, because greeting was unset
  stdout.println(greeting)            // "Hello, World"
  greeting :=? "Goodbye"              // SKIPPED, because greeting is now set
  stdout.println(greeting)            // still "Hello, World"

AFTER THE FIRST GUARDED ASSIGNMENT, THE VARIABLE IS SET AND ALL FUTURE :=? CALLS ARE NO-OPS. This is exactly what 'assign default if unset' should mean.

WHY NOT IF + ASSIGNMENT?

The long-form equivalent works but is more verbose:

  if not greeting?
    greeting: "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 guarded assignment (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 guarded assignment. See Q1265 for Date. See Q1266 for custom record. See Q1226 for the contrast between :=? and <?.

Example

defines module qa.operators.guardedassignstring

  defines function

    //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()

      greeting as String: String()
      greeting :=? "Hello, World"
      stdout.println(`first :=? : ${greeting}`)

      greeting :=? "Goodbye"
      stdout.println(`second :=? : ${greeting}`)

      // Variable that already has a value at declaration
      title <- loadStoredTitle()
      title :=? "Default Title"
      stdout.println(`pre-set :=? : ${title}`)

Common mistakes

E50001 — :=? is guarded ASSIGNMENT (assign if unset). <? is coalescing MINIMUM (returns the smaller value). They are completely different operators and serve different purposes. See ek9 -h E50001 for details.

Incorrect:

greeting <? "Hello, World"

Correct:

greeting :=? "Hello, World"
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: unset, fallback, guarded assignment, :=?, String, default value, conditional assignment