API Stability Notice
Macroforge is under active development. The API is not yet stable and may change between versions. Some documentation sections may be outdated.
PartialOrd
The PartialOrd macro generates a compareTo() method for partial ordering comparison. This is analogous to Rust’s PartialOrd trait, enabling comparison
between values where some pairs may be incomparable.
Generated Output
| Type | Generated Code | Description |
|---|---|---|
| Class | classNamePartialCompare(a, b) + static compareTo(a, b) | Standalone function + static wrapper method |
| Enum | enumNamePartialCompare(a: EnumName, b: EnumName): Option<number> | Standalone function returning Option |
| Interface | interfaceNamePartialCompare(a: InterfaceName, b: InterfaceName): Option<number> | Standalone function with Option |
| Type Alias | typeNamePartialCompare(a: TypeName, b: TypeName): Option<number> | Standalone function with Option |
Return Values
Unlike Ord, PartialOrd returns an Option<number> to handle incomparable values:
- Option.some(-1):
ais less thanb - Option.some(0):
ais equal tob - Option.some(1):
ais greater thanb - Option.none(): Values are incomparable
When to Use PartialOrd vs Ord
PartialOrd: When some values may not be comparable
- Example: Floating-point NaN values
- Example: Mixed-type unions
- Example: Type mismatches between objects
Ord: When all values are guaranteed comparable (total ordering)
Comparison Strategy
Fields are compared lexicographically in declaration order:
- Compare first field
- If incomparable, return
Option.none() - If not equal, return that result wrapped in
Option.some() - Otherwise, compare next field
- Continue until a difference is found or all fields are equal
Type-Specific Comparisons
| Type | Comparison Method |
|---|---|
number/bigint | Direct comparison, returns some() |
string | localeCompare() wrapped in some() |
boolean | false < true, wrapped in some() |
| null/undefined | Returns none() for mismatched nullability |
| Arrays | Lexicographic, propagates none() on incomparable elements |
Date | Timestamp comparison, none() if invalid |
| Objects | Unwraps nested Option from compareTo() |
Field-Level Options
The @ord decorator supports:
skip- Exclude the field from ordering comparison
Example
Before (Your Code)
/** @derive(PartialOrd) */
class Temperature {
value: number | null;
unit: string;
} After (Generated)
class Temperature {
value: number | null;
unit: string;
static compareTo(a: Temperature, b: Temperature): number | null {
return temperaturePartialCompare(a, b);
}
}
export function temperaturePartialCompare(a: Temperature, b: Temperature): number | null {
if (a === b) return 0;
const cmp0 = (() => {
if (typeof (a.value as any)?.compareTo === 'function') {
const optResult = (a.value as any).compareTo(b.value);
return optResult === null ? null : optResult;
}
return a.value === b.value ? 0 : null;
})();
if (cmp0 === null) return null;
if (cmp0 !== 0) return cmp0;
const cmp1 = a.unit.localeCompare(b.unit);
if (cmp1 === null) return null;
if (cmp1 !== 0) return cmp1;
return 0;
}Generated output:
class Temperature {
value: number | null;
unit: string;
static compareTo(a: Temperature, b: Temperature): number | null {
return temperaturePartialCompare(a, b);
}
}
export function temperaturePartialCompare(a: Temperature, b: Temperature): number | null {
if (a === b) return 0;
const cmp0 = (() => {
if (typeof (a.value as any)?.compareTo === 'function') {
const optResult = (a.value as any).compareTo(b.value);
return optResult === null ? null : optResult;
}
return a.value === b.value ? 0 : null;
})();
if (cmp0 === null) return null;
if (cmp0 !== 0) return cmp0;
const cmp1 = a.unit.localeCompare(b.unit);
if (cmp1 === null) return null;
if (cmp1 !== 0) return cmp1;
return 0;
}Required Import
The generated code automatically adds an import for Option from macroforge/utils.