Sort two Measurement records by value and pick the smaller using <? coalescing.

← Operators and Expressions · Ref: Q1230

Define a custom <=> operator that compares only the reading field. The <? coalescing operator then uses that ordering.

  operator <=> as pure
    -> other as Measurement
    <- rtn as Integer: reading <=> other.reading
  smallerReading() as pure
    -> left as Measurement, right as Measurement
    <- rtn as Measurement: left <? right

The custom <=> means <? compares by reading only, ignoring label. This is different from 'default operator' which would compare label first (alphabetically), then reading.

See Q1228 for default operator ordering. See Q1229 for priority-based custom ordering.

Example

defines module qa.operators.coalesce.customcmp

  defines record

    Measurement
      label as String: String()
      reading as Float: 0.0

      Measurement()
        ->
          label as String
          reading as Float
        this.label: label
        this.reading: reading

      operator <=> as pure
        -> other as Measurement
        <- rtn as Integer: reading <=> other.reading

      default operator

  defines function

    smallerReading() as pure
      ->
        left as Measurement
        right as Measurement
      <- rtn as Measurement: left <? right

  defines program

    CoalesceCustomCmpDemo()
      stdout <- Stdout()

      // === BOTH SET — compares by reading only ===

      sensorA <- Measurement("Station-North", 22.5)
      sensorB <- Measurement("Station-South", 18.3)
      smaller <- smallerReading(sensorA, sensorB)
      stdout.println(`Smaller reading: ${smaller}`)

      // === ONE UNSET — returns the set one ===

      calibrated <- Measurement("Lab", 15.0)
      uncalibrated <- Measurement()
      usable <- smallerReading(calibrated, uncalibrated)
      stdout.println(`Usable reading: ${usable}`)

      // === BOTH UNSET — result is unset ===

      offlineA <- Measurement()
      offlineB <- Measurement()
      noReading <- smallerReading(offlineA, offlineB)
      if noReading?
        stdout.println(`Reading: ${noReading}`)
      else
        stdout.println("No reading available")

      // === >? FOR LARGER READING ===

      larger <- sensorA >? sensorB
      stdout.println(`Larger reading: ${larger}`)
Other ways to ask this
  • How do I coalesce custom records that compare by a specific field?
  • Given two sensor measurements, select the one with the lower reading.
  • In Java I'd use Comparator.comparingDouble for a single field. What does EK9 offer?
  • Migrating from Rust where I derive Ord on a struct — how does EK9 do custom ordering?

Coming from another language?

Java: Comparator.comparingDouble(Measurement::getReading). Python: min(a, b, key=lambda m: m.reading). Rust: impl Ord comparing self.reading. EK9: custom <=> on reading field, then left <? right.

Keywords: coalescing, <?, custom, record, minimum, measurement, reading, comparison