Does ':=?' evaluate its right-hand side when the target is already set?

← Control Flow · Ref: Q1961

No. The right-hand side of ':=?' runs ONLY when its target is unset (or not yet initialised). When the target is already set, ':=?' neither assigns nor evaluates the right side - so a call there is not made at all. That makes ':=?' the assignment form of the coalescing '?:': 'found :=? value' is 'found := found ?: value'.

THE TWO OPERATORS

  count := value     ALWAYS assign value to count (an unset value included)
  found :=? value    assign value to found ONLY if found is unset; otherwise value is not even evaluated

RECORD WHETHER AN ASSIGNMENT GAVE A VALUE, BUT ONLY THE FIRST TIME
Assign, then fall back with ':=?' on the set-check:

  count := fetchCount()   // count is always assigned
  found :=? count?        // found takes the result only if it had none

An assignment never yields a value in EK9, so the set-check is its own step.

A ':=?' ON A TARGET THE COMPILER KNOWS IS SET IS AN ERROR

If flow analysis proves the target is already set ('found <- false' then 'found :=? count?'), the ':=?' can never apply, and the compiler rejects it (E08095).

See Q1960 for re-pointing inside a safe access block (E08040), Q79 for ':=?' and Q759 for assigning and then checking.

Example

defines module qa.flow.guard.assignifunset

  defines function

    fetchCount()
      <- rtn as Integer: 42

    alreadyFound()
      <- rtn as Boolean: false

  defines program

    GuardThenAssignIfUnsetDemo()
      stdout <- Stdout()

      count <- Integer()
      found <- Boolean()
      stdout.println(`before: count set=${count?} found set=${found?}`)

      count := fetchCount()
      found :=? count?
      stdout.println(`after: count=${count} count set=${count?} found=${found}`)

      found :=? alreadyFound()
      stdout.println(`second: found=${found}`)

      stdout.println("Done")

Common mistakes

E08095 — Declared with a value, found is provably set, so the following 'found :=? count?' can never apply. Declare it unset ('Boolean()') when ':=?' is meant to fill it. See ek9 -h E08095 for details.

Incorrect:

      found <- false

Correct:

      found <- Boolean()
Other ways to ask this
  • Is the right of ':=?' evaluated lazily?
  • How do I record whether an assignment gave a value, but only the first time?
  • How do I combine assign-if-unset with a set-check?
  • When does ':=?' call the function on its right?
  • What does error E08095 mean for ':=?'?

Coming from another language?

C#: 'found ??= Compute()' only evaluates Compute() when found is null, the same laziness as EK9's ':=?'. Kotlin: 'found = found ?: compute()'. JavaScript: 'found ??= compute()'. EK9: 'found :=? compute()' - and because an assignment never yields a value, a set-check of another assignment is written as its own step: 'count := fetchCount()' then 'found :=? count?'.

Keywords: assign if unset, set check, E08095, lazy, conditional evaluation, boolean, :=?