Find the cheaper Product from two optional product records.
← Operators and Expressions · Ref: Q1228
Define 'default operator' on the record to auto-generate <=> from fields (compared in declaration order). Then use <? in a pure function to pick the lesser product.
cheaperProduct() as pure -> left as Product, right as Product <- rtn as Product: left <? right
The <? coalescing minimum operator works on ANY type that has <=> defined — not just built-in types. If both values are set, <? returns the lesser. If one is unset, it returns the set one. If both are unset, the result is unset.
See Q1049 for comparison operators on records. See Q1208 for <? on built-in Date type.
Example
defines module qa.operators.coalescemin.record defines record Product name as String: String() price as Float: 0.0 Product() -> name as String price as Float this.name: name this.price: price default operator defines function cheaperProduct() as pure -> left as Product right as Product <- rtn as Product: left <? right defines program CoalesceMinRecordDemo() stdout <- Stdout() // === BOTH SET — returns the lesser by natural ordering === widget <- Product("Widget", 19.99) gadget <- Product("Gadget", 29.99) cheaper <- cheaperProduct(widget, gadget) stdout.println(`Cheaper product: ${cheaper}`) // === ONE UNSET — returns the set one === knownProduct <- Product("Keyboard", 49.99) unknownProduct <- Product() available <- cheaperProduct(knownProduct, unknownProduct) stdout.println(`Available product: ${available}`) // === BOTH UNSET — result is unset === missingA <- Product() missingB <- Product() noProduct <- cheaperProduct(missingA, missingB) if noProduct? stdout.println(`Product: ${noProduct}`) else stdout.println("No product available") // === INLINE USAGE === monitor <- Product("Monitor", 299.99) mouse <- Product("Mouse", 12.99) bestDeal <- monitor <? mouse stdout.println(`Best deal: ${bestDeal}`)
Common mistakes
E07235 — The <? operator requires <=> to be defined. Use 'default operator' on the record to auto-generate it. See ek9 -h E15200 for details.
Incorrect:
//no operator defined
Correct:
default operator
Other ways to ask this
- How do I use <? coalescing on a custom record type?
- Given two optional Product records, pick the one with the lower natural ordering.
- In Java I'd use Comparator.comparing(Product::getPrice). How does EK9 handle this?
- Migrating from Kotlin where I use minOf(a, b) for data classes — what does EK9 offer?
Coming from another language?
Java: Stream.of(a, b).filter(Objects::nonNull).min(Comparator.naturalOrder()). Python: min(a, b, key=lambda p: (p.name, p.price)). Kotlin: minOf(a, b). EK9: left <? right — one operator, handles unset automatically.
Keywords: <?, coalescing, default operator, user-defined, cheaper, minimum, product, record