How do coalescing operators handle unset values in EK9?

← Operators and Expressions · Ref: Q243

EK9 provides coalescing operators that safely handle unset values. These are expression-level operators (not implementable on your types) that the compiler handles directly.

NULL COALESCING (??)

The ?? operator checks if the left operand exists in memory. If the left is invalid (absent), it returns the right.

  result <- possiblyAbsent ?? fallback

This is a memory-level check, not an isSet check.

COALESCE (ELVIS) (?:)

The ?: operator checks if the left is both present AND set (via isSet). If the left is absent or unset, it returns the right.

  result <- possiblyUnset ?: fallback

This is more thorough than ?? because it also checks the isSet state.

COMPARISON COALESCING OPERATORS

These operators compare two values, handling the case where one or both might be unset:

  <? (lesser coalescing): returns the lesser value, or whichever is valid
  <=? (lesser-or-equal coalescing): returns the lesser-or-equal value
  >? (greater coalescing): returns the greater value, or whichever is valid
  >=? (greater-or-equal coalescing): returns the greater-or-equal value

Priority rules: If left is invalid, return right. If right is invalid, return left. If both valid, compare and return the winner.

  minimum <- a <? b
  maximum <- a >? b

NOTE: :=? is NOT a coalescing operator. It is an ASSIGNMENT operator that assigns only if the target is unset. See Q1152 for :=? details. See Q1226 for <? vs :=? contrast.

REPLACING VERBOSE IF/ELSE

Without coalescing:

  if a?
    if b?
      if a < b
        result: a
      else
        result: b
    else
      result: a
  else
    result: b

With coalescing:

  result <- a <? b

One line replaces a nested if/else tree.

See Q75 for guard switch. See Q161 for Dict safe access. See Q168 for fallback values. See Q239 for comparison operators. See Q238 for the complete operator set.

Example

defines module qa.operators.coalescing

  defines program

    CoalescingDemo()
      stdout <- Stdout()

      // === ELVIS OPERATOR ?: ===

      name <- String()
      displayName <- name ?: "Anonymous"
      stdout.println(`Display name: ${displayName}`)

      // === COMPARISON COALESCING OPERATORS ===

      a <- 10
      b <- 20
      minimum <- a <? b
      maximum <- a >? b
      stdout.println(`Minimum: ${minimum}`)
      stdout.println(`Maximum: ${maximum}`)

      // === CHAINED COALESCING ===

      primary <- String()
      secondary <- String()
      fallback <- "last-resort"
      chosen <- primary ?: secondary ?: fallback
      stdout.println(`Chosen: ${chosen}`)

Common mistakes

E50030 — The result of a coalescing operator is one of its operands, so the operands must share a type. A String and an Integer share only Any, which is rejected at the operator (E50030). See ek9 -h E50030 for details.

Incorrect:

displayName <- name ?: 42

Correct:

displayName <- name ?: "Anonymous"
Other ways to ask this
  • What is the null coalescing operator in EK9?
  • How does the Elvis operator work in EK9?
  • How do I pick the lesser or greater of two possibly unset values?
  • What are the coalescing operators in EK9?
  • How do coalescing comparators work in EK9?
  • What do <? and >? mean in EK9?
  • What is the ?? operator in EK9?
  • How does null coalescing work in EK9?

Coming from another language?

Java: ternary x != null ? x : default, no null coalescing operator, Optional.orElse(). Python: x if x is not None else default, or shorthand x or default (falsy gotcha). Rust: unwrap_or(), unwrap_or_else(), no null concept. Go: if x == nil then default, no ternary or coalescing. Kotlin: ?: elvis operator for null, no comparison coalescing operators. Swift: ?? nil coalescing (same operator as EK9!), no equivalent of ?: isSet check or <? >? comparison coalescing operators. JavaScript: ?? null coalescing (ES2020), || logical or fallback. EK9: ?? (memory check), ?: (isSet check), <? <=? >? >=? (comparison coalescing operators), :=? (assign-if-unset).

Keywords: fallback, assignment, operator, migrate, coalescing, lesser, maximum, comparator, coalesce, unset, default, null, greater, expression, absent, conditional, isset, safe, elvis, swift, minimum