Why must I put parentheses around '?:' when I mix it with '+' or '=='?

← Operators and Expressions · Ref: Q2060

EK9 never lets a coalescing operator - null coalescing '??', coalesce (Elvis) '?:' or the comparison coalescing operators '<?', '<=?', '>?', '>=?' - mix with a different binary operator without parentheses (E08269). '?:' binds tighter than arithmetic, comparison and logic, so 'first + second ?: fallback' would be 'first + (second ?: fallback)': when first is unset the sum is unset and the fallback never applied to it. Kotlin puts '?:' below '+', C# and JavaScript put '??' below '==', so whichever reading you expect, a reader from another language expects the other. The parentheses say which one you mean:

  sumOrFallback <- (first + second) ?: fallback
  firstPlusEither <- first + (second ?: fallback)

A chain of the SAME operator reads one way only and needs no parentheses:

  chained <- first ?: second ?: fallback

'?:' and '??' are different operators, so 'first ?: second ?? fallback' needs parentheses too, and so does 'first ?? second + fallback' - write '(first ?? second) + fallback'.

See Q1091 for the coalescing operator (Elvis), Q1093 for chained coalescing and Q2061 for the operand types coalescing accepts.

Example

defines module qa.operators.coalescingparentheses

  defines function

    reading()
      -> start as Integer
      <- rtn as Integer: start

  defines program

    CoalescingParenthesesDemo()
      stdout <- Stdout()
      first <- reading(Integer())
      second <- reading(5)
      fallback <- reading(100)

      //The sum is unset because first is unset, so the fallback is taken.
      sumOrFallback <- (first + second) ?: fallback
      stdout.println(`(first + second) ?: fallback = ${sumOrFallback}`)

      //Only second is defaulted; first is still unset, so the sum is unset.
      firstPlusEither <- first + (second ?: fallback)
      firstSet <- firstPlusEither?
      stdout.println(`first + (second ?: fallback) is set: ${firstSet}`)

      chained <- first ?: second ?: fallback
      stdout.println(`first ?: second ?: fallback = ${chained}`)

Common mistakes

E08269 — '?:' binds tighter than '+', so this would be 'first + (second ?: fallback)'. Parenthesise whichever operator is meant to apply first. See ek9 -h E08269 for details.

Incorrect:

      sumOrFallback <- first + second ?: fallback

Correct:

      sumOrFallback <- (first + second) ?: fallback

E08269 — '?:' and '??' are different operators, so mixing them needs parentheses; a chain of the same operator does not. See ek9 -h E08269 for details.

Incorrect:

      chained <- first ?: second ?? fallback

Correct:

      chained <- first ?: second ?: fallback
Other ways to ask this
  • What does error E08269 mean?
  • Does '?:' bind tighter than '+' in EK9?
  • Why is 'total <- first + second ?: fallback' a compile error?
  • Can I mix '<?' with arithmetic without brackets?
  • What is the precedence of the Elvis operator in EK9?

Coming from another language?

Kotlin: 'a + b ?: c' is '(a + b) ?: c' ('?:' is below '+'). C#/JavaScript: 'a + b ?? c' is '(a + b) ?? c', and JavaScript made mixing '??' with '||'/'&&' a SyntaxError. EK9 applies that JavaScript rule to '??', '?:' and the '<?' family against every other binary operator.

Keywords: operator mixing, <?, coalescing, E08269, elvis, parentheses, ?:, precedence