E01096: '??' and '??=' are not EK9 operators - EK9 is designed so that a value is never absent (it may be unset), so there is nothing for them to test; write 'x ?: y' to use 'y' when 'x' is unset, and 'x :=? y' to assign 'y' only when 'x' is unset

Category: E01xxx Parsing and excluded constructs · Reported in phase: SYMBOL_DEFINITION · Classification: NULL_COALESCING_OPERATOR_REMOVED · All error codes

What went wrong

'??' is not an EK9 operator. In languages that have it (C#, JavaScript, Swift, PHP) it means "the left side, unless it is null, then the right side". EK9 is designed so that a value is never absent - there is no null, and a variable must be given a value before it is read, though that value may be unset. So '??' is not needed: 'x ?? y' would never use 'y' - it would give 'x' whatever 'y' is:

rtn: name ?? fallback //removed: write '?:', which uses the right when the left is unset

'??=' (C#, JavaScript, PHP: assign when the left is null) is removed for the same reason and gives this same error, naming ':=?' (assign only if unset):

rtn ??= fallback //removed: write ':=?', which assigns only when the left is unset

The grammar does not drop the '??' and '??=' tokens - it keeps them only to give this error, as it keeps 'break' and 'return'.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1059 (Why is there no ?? null coalescing operator in EK9, and how do I default an unset value?)
  • ek9 -q 243 (How do coalescing operators handle unset values in EK9?)
  • ek9 -q 1091 (Provide a fallback display name when the user name is unset.)

How to fix it

Decide what the left side may be, then write that.

If the left may be UNSET (a value that exists but has no meaningful value, such as an Integer() or a String()), use '?:' - it gives the left when it is set and the right when it is not:

rtn: name ?: fallback

To give an existing variable a value only when it is unset, use ':=?' instead of the removed '??=':

rtn :=? fallback

If the left is an Optional or a Result, check it with '?' first, then read it.

This is a suggestion, never an automatic repair: '??' and '??=' look only for an absent left, while '?:' and ':=?' look for an unset one, so they do not always mean the same, and the compiler will not choose for you.

Common causes

  • Habit from C#, JavaScript, Swift or PHP, where '??' supplies a default (Kotlin writes it '?:', as EK9 does)
  • Habit from C#, JavaScript or PHP, where 'x ??= y' assigns 'y' when 'x' is null
  • AI-written code that has not met EK9 before
  • Reading 'null coalescing' in another language's notes and expecting the same operator

See also

  • E01095: '<=?' and '>=?' were removed (the other removed coalescing operators)
  • E08269: a coalescing operator mixed with a different binary operator without parentheses
  • E08094: a literal on the left of '?:' - it is always set, so the right is never used
  • https://ek9.io/operators.html#coalescing

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E01096: '??' and '??=' are not EK9 operators - EK9 is designed so that a value is never absent (it may be unset), so there is nothing for them to test; write 'x ?: y' to use 'y' when 'x' is unset, and 'x :=? y' to assign 'y' only when 'x' is unset

#!ek9
defines module error.e01096.bad

  defines function

    nameOrDefault()
      ->
        name as String
        fallback as String
      <- rtn as String?

      rtn: name ?? fallback //removed: write '?:', which uses the right when the left is unset

    nameOrAssigned()
      ->
        name as String
        fallback as String
      <- rtn as String: name

      rtn ??= fallback //removed: write ':=?', which assigns only when the left is unset

Fixed: Write '?:' - it gives the right when the left is unset

#!ek9
defines module error.e01096.good

  defines function

    nameOrDefault()
      ->
        name as String
        fallback as String
      <- rtn as String?

      rtn: name ?: fallback

Fixed: Write ':=?' - it assigns only when the left is unset

#!ek9
defines module error.e01096.good.assign

  defines function

    nameOrDefault()
      ->
        name as String
        fallback as String
      <- rtn as String: name
      rtn :=? fallback

Questions and answers

These Q&A entries show E01096 and its fix: