How do I use a guard expression as a standalone assignment that returns Boolean?

← Control Flow · Ref: Q759

The guard expression operator (?=) can be used as a standalone assignment expression that returns a Boolean. The expression evaluates the right-hand side (RHS), checks if it is SET, and if so assigns it to the target variable. The Boolean result indicates whether the assignment occurred.

STANDALONE GUARD EXPRESSION

Unlike using ?= in control flow guards (if, while), this pattern captures the Boolean result:

  name as String?
  guardResult <- name ?= getValue()

Here guardResult is a Boolean: true if getValue() returned a set value (and name was assigned), false otherwise.

WHEN THE RHS IS SET

If getValue() returns a set String, the assignment happens and the result is true:

  name as String?
  result <- name ?= getValue()
  // result is true, name is now the returned value

WHEN THE RHS IS UNSET

If the RHS returns an unset value, no assignment occurs and the result is false:

  existing <- "already here"
  result <- existing ?= getEmpty()
  // result is false, existing is unchanged

KEY DIFFERENCE FROM :=? AND ?= IN GUARDS

The three related patterns:

  name :=?  checks TARGET isSet, assigns if unset
  if name ?=  checks SOURCE isSet, controls flow
  result <- name ?=  checks SOURCE isSet, returns Boolean

The standalone form returns Boolean rather than controlling a block.

See Q74 for guard variables in if. See Q77 for guard variables in while. See Q79 for guarded assignment :=? operator.

Example

defines module qa.flow.guard.expression.boolean

  defines function

    getValue()
      <- rtn <- String()
      rtn: "hello"

    getEmpty()
      <- rtn <- String()

  defines program

    GuardExpressionBooleanDemo()
      stdout <- Stdout()

      // === GUARD EXPRESSION WITH SET RHS ===

      name as String?
      guardResult <- name ?= getValue()
      stdout.println(guardResult)
      stdout.println(name)

      // === GUARD EXPRESSION WITH UNSET RHS ===

      existing <- "already here"
      guardResult2 <- existing ?= getEmpty()
      stdout.println(guardResult2)
      stdout.println(existing)

      stdout.println("Done")

Common mistakes

E50030 — The guard expression ?= returns a Boolean, not the type of the assigned value. Declaring the result as String triggers E50030 because Boolean and String are incompatible. Use type inference with '<-' to get the correct Boolean type. See ek9 -h E50030 for details.

Incorrect:

guardResult as String: name ?= getValue()

Correct:

guardResult <- name ?= getValue()
Other ways to ask this
  • Can I use ?= outside of an if or while statement?
  • How does the guard expression ?= return a Boolean result?
  • What does guardResult <- name ?= getValue() do in EK9?

Coming from another language?

Java: No direct equivalent. Must write: 'Optional<String> opt = getValue(); boolean assigned = opt.isPresent(); if (assigned) name = opt.get();'. Three separate statements. Kotlin: 'val result = getValue()?.also { name = it } != null'. Requires nullable types and scope functions. Python: 'result = getValue(); assigned = result is not None; name = result if assigned else name'. Manual and error-prone. EK9: 'result <- name ?= getValue()' is a single expression combining assignment and Boolean result.

Keywords: assignment, conditional, return, operator, null-safe, boolean, expression, standalone, guard, safe, source, result, check, isset