Why is there no ?? null coalescing operator in EK9, and how do I default an unset value?

← Operators and Expressions · Ref: Q1059

EK9 has no ?? operator (E01096), and no null keyword. Its tri-state model has THREE states - ABSENT (no object at all), present but UNSET (an object with no meaningful value, e.g. String()), and SET. EK9 is designed so that a value you declare and assign is never absent, so a null-test like ?? would have nothing to find: 'name ?? "default"' would give name, whatever the default was.

To default an UNSET value use ?: (COALESCE, Elvis):

  name <- String()              present, but UNSET
  b <- name ?: "stranger"       b is "stranger"

?: returns name only if it is SET. If name is unset (or absent) the result is "default". To assign a default in place, use :=? (nickname :=? "stranger"); there is no ??= either (E01096).

The distinction from other languages:

  Java: name != null ? name : "default"     checks null
  Kotlin: name ?: "default"                  checks null
  Swift, C#, JavaScript: name ?? "default"  checks null; there is no such operator in EK9
  EK9: name ?: "default"                     checks isSet

In EK9, a variable can exist but be UNSET. This is not the same as null. An unset String is a String object that has no meaningful value yet. It still exists - you can call ? on it to check.

  name <- String()                // name exists, but is UNSET
  stdout.println(`${name?}`)      // false (unset)
  name := "Alice"                 // now SET
  stdout.println(`${name?}`)      // true

If BOTH sides of ?: are unset, the result is unset.

See Q243 for all coalescing operators. See Q898 for ? is not ternary. See Q883 for tri-state semantics.

Example

#!ek9
defines module qa.operators.coalescingisset

  defines function

    //No nickname saved: unset
    savedNickname()
      <- rtn as String: String()

  defines program

    CoalescingDemo()
      stdout <- Stdout()

      //An UNSET String: the object is present, it just has no value
      name <- String()
      stdout.println(`name is set: ${name?}`)

      //?: tests SET-NESS - name is unset, so the default is used
      defaulted <- name ?: "stranger"
      stdout.println(`Hello ${defaulted}`)

      //Set the name
      name := "Alice"
      stdout.println(`name is set: ${name?}`)

      //Now name is set, so ?: returns it
      viaElvis <- name ?: "visitor"
      stdout.println(`Hello ${viaElvis}`)

      //:=? gives an unset variable a value, and leaves a set one alone
      nickname <- savedNickname()
      nickname :=? "stranger"
      stdout.println(`nickname is ${nickname}`)

Common mistakes

E01096 — '??' is not an EK9 operator: EK9 is designed so that a value is never absent (it may be unset), so '??' would have nothing to test. '?:' gives the right when the left is unset. See ek9 -h E01096 for details.

Incorrect:

      defaulted <- name ?? "stranger"

Correct:

      defaulted <- name ?: "stranger"

E01096 — '??=' is not an EK9 operator either: EK9 is designed so that a value is never absent (it may be unset), so there is nothing for it to test. ':=?' assigns only when the variable is unset. See ek9 -h E01096 for details.

Incorrect:

      nickname ??= "stranger"

Correct:

      nickname :=? "stranger"

E01073 — 'null' does not exist in EK9 — a variable is either set or unset (tri-state), so assign a real value rather than 'null'. See ek9 -h E01073 for details.

Incorrect:

      name := null

Correct:

      name := "Alice"
Other ways to ask this
  • Why is ?? not an operator in EK9?
  • What do the coalescing operators actually check in EK9?
  • Why can't I write name ?? "default" in EK9?
  • Should I use ?: or :=? to default an unset value in EK9?
  • Does ?: check for null in EK9?
  • Is there a ??= null coalescing assignment in EK9?

Coming from another language?

Java: ternary with null check. Kotlin: ?: (elvis) checks null. JavaScript/C#/Swift: ?? (nullish coalescing) checks null - EK9 has no ?? or ??= (E01096). EK9: ?: checks that the value is SET - use ?: to default an unset value. EK9 has no null.

Keywords: absent, coalescing, default, elvis, isset, null, operator, unset