How does the <? coalescing minimum operator work in EK9?

← Operators and Expressions · Ref: Q963

The <? operator is a COALESCING MINIMUM. It returns the smaller of two SET values. If either value is UNSET, the result depends on which is set:

- Both SET: returns the smaller value (like Math.min)
- Left SET, right UNSET: returns left (the SET one)
- Left UNSET, right SET: returns right (the SET one)
- Both UNSET: returns UNSET

This is unique to EK9 — no other language has coalescing comparators.

The >? operator is the coalescing MAXIMUM (same rules but returns the larger).

Bridge: There is no equivalent in Java, Python, Kotlin, or Rust. The closest analogy is a null-safe Math.min() that automatically handles missing values.

See Q899 for the full coalescing operator family. See Q24 for tri-state semantics.

Example

defines module qa.operators.coalescingmin

  defines function

    <?-
      Find the cheaper price using <? coalescing minimum.
      If either price is unset, returns the other.
    -?>
    bestDeal() as pure
      ->
        onlinePrice as Float
        storePrice as Float
      <-
        rtn as Float: onlinePrice <? storePrice

    <?-
      Find the closest deadline using <? coalescing minimum on dates.
      Works with any type that has the <=> comparison operator.
    -?>
    nearestDeadline() as pure
      ->
        deadlineA as Date
        deadlineB as Date
      <-
        rtn as Date: deadlineA <? deadlineB

    maxOf() as pure
      ->
        left as Integer
        right as Integer
      <- rtn as Integer: left >? right

    chainedMinFloat() as pure
      ->
        a as Float
        b as Float
        c as Float
      <- rtn as Float: a <? b <? c

  defines program

    CoalescingMinDemo()
      stdout <- Stdout()

      // Both values set — returns the smaller
      cheapest <- bestDeal(29.99, 19.99)
      stdout.println(`Cheapest: ${cheapest}`)

      // One value unset — returns the SET one
      knownPrice <- 25.0
      unknownPrice <- Float()
      safePrice <- knownPrice <? unknownPrice
      stdout.println(`Safe price: ${safePrice}`)

      // Both unset — result is unset
      noPrice1 <- Float()
      noPrice2 <- Float()
      noResult <- noPrice1 <? noPrice2
      if noResult?
        stdout.println("Should not reach here")
      else
        stdout.println("Both unset, result is unset")

      // >? coalescing maximum — returns the larger
      topScore <- maxOf(85, 92)
      stdout.println(`Top score: ${topScore}`)

      // Chaining: find minimum of three values
      lowest <- chainedMinFloat(30.0, 15.0, 22.0)
      stdout.println(`Lowest of three: ${lowest}`)

Common mistakes

E50001 — EK9 has no min() function so the call cannot be resolved — use the <? coalescing minimum operator instead. See ek9 -h E50001 for details.

Incorrect:

min(onlinePrice, storePrice)

Correct:

onlinePrice <? storePrice
Other ways to ask this
  • What does <? do in EK9?
  • How do I find the minimum of two values safely in EK9?
  • What is EK9's safe minimum operator?
  • How does coalescing less-than work?

Coming from another language?

Java: Math.min(a, b) requires null checks. Python: min(a, b) throws on None. Kotlin: minOf(a, b) requires ?: null handling. EK9's <? combines comparison + unset handling in one operator.

Keywords: unset handling, coalescing, safe comparison, less than, minimum