What type does 'a ?: b' have when a and b are different types?

← Operators and Expressions · Ref: Q2061

Every operator that returns ONE of its operands - '?:', '??', '<?', '<=?', '>?', '>=?' and the ternary 'condition <- this else that' - gives a type the operands already share:
1. the same type, or a subtype (including Any when an operand is DECLARED as Any)
2. one operand promoting ('#^') to the other's type - an Integer and a Float give a Float, and only the operand chosen is promoted
3. otherwise their nearest common super class or trait

Never an invented Any and never a third type: operands that share nothing but Any are a compile error at the operator (E50030), and so are two types that promote to each other or two unrelated nearest traits. The answer never depends on which operand you write first.

Give the operands one type - convert one explicitly ('$count' is a String):

  shown <- $count ?: label

An 'Optional of Integer' is not an Integer, so 'maybeCount ?: 0' is E50030 too; use getOrDefault:

  chosen <- maybeCount.getOrDefault(0)

A literal on the LEFT of '?:' is always set, so the right would never be used (E08094) - put the value that may be unset on the left:

  promoted <- count ?: amount

See Q1091 for the coalescing operator (Elvis) and Q2060 for mixing '?:' with other operators.

Example

defines module qa.operators.coalescingtypes

  defines function

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

    labelOf()
      -> start as String
      <- rtn as String: start

  defines program

    CoalescingTypesDemo()
      stdout <- Stdout()
      count <- countOf(Integer())
      amount <- 2.5
      label <- labelOf("none")

      //Integer promotes to Float, so the result is a Float.
      promoted <- count ?: amount
      stdout.println(`count ?: amount = ${promoted}`)

      shown <- $count ?: label
      stdout.println(`string of count ?: label = ${shown}`)

      maybeCount <- Optional(countOf(3))
      chosen <- maybeCount.getOrDefault(0)
      stdout.println(`maybeCount.getOrDefault(0) = ${chosen}`)

Common mistakes

E50030 — An Integer and a String share no type but Any, and the result is one of them. Convert one operand so both have one type. See ek9 -h E50030 for details.

Incorrect:

      shown <- count ?: label

Correct:

      shown <- $count ?: label

E50030 — An 'Optional of Integer' is not an Integer, so '?:' has no type to give. Use the Optional's own getOrDefault. See ek9 -h E50030 for details.

Incorrect:

      chosen <- maybeCount ?: 0

Correct:

      chosen <- maybeCount.getOrDefault(0)

E08094 — A literal on the left of '?:' is always set, so it is always the result and the right is never used. Put the value that may be unset on the left. See ek9 -h E08094 for details.

Incorrect:

      promoted <- 7 ?: amount

Correct:

      promoted <- count ?: amount
Other ways to ask this
  • Why is 'count ?: label' a compile error when count is an Integer and label a String?
  • Can a ternary return an Integer in one branch and a String in the other?
  • What does E50030 mean on a coalescing operator?
  • Does EK9 promote Integer to Float in a coalescing expression?
  • How do I default an Optional with '?:'?

Coming from another language?

Kotlin: 'if (c) 1 else "x"' and '1 ?: "x"' are typed Any (Comparable & Serializable). Java: 'c ? 1 : "x"' is an intersection type or Object. EK9 rejects operands with nothing but Any in common, so the result can always be used as the type of its operands without a cast (EK9 has none).

Keywords: E50030, ??, coalescing, promotion, E08094, ternary, common type, ?:, common supertype