66 Ordering
Some Predica operations require a stable comparison on values, such as ordered comparison
operators, ordered indexes, deterministic rendering, and query operations such as orderBy.
A type is ordered when the language defines a standard order relation ≤ on
its values. This relation is the type's canonical comparison; it is total and
non-exceptional.
Ordering is a semantic property of a type. It does not change the type's carrier set, equality relation, subtype relationships, or identity rules. It only says how two values of the same type are compared when an ordered comparison is required.
Order axioms
For an ordered type T, the standard order relation ≤ is a binary relation on values of
type T. For all x, y, and z of type T, it must satisfy:
- Reflexivity:
x ≤ x. - Antisymmetry:
x ≤ y ∧ y ≤ x ⇒ x = y. - Transitivity:
x ≤ y ∧ y ≤ z ⇒ x ≤ z. - Totality:
x ≤ y ∨ y ≤ x.
The relation is also non-exceptional: for any two values of the same ordered type, x ≤ y
is defined and evaluates to a Boolean value.
Derived comparison operators
The standard order operator ≤ is the primitive comparison associated with an ordered type.
The other ordered comparison operators are derived from ≤ and equality:
x < yiffx ≤ y ∧ x ≠ y.x ≥ yiffy ≤ x.x > yiffy ≤ x ∧ x ≠ y.
These derived operators do not add separate ordering semantics. They are available exactly
when the standard order relation ≤ is available for the compared values.
Default ordering and opt-out
When a declaration kind has an implicit default order, the type is ordered by default. The
unordered modifier suppresses that default order and is written just before the nominal type
name. It is valid only when the declaration would otherwise have an implicit default order.
enum Month
{
jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
}
value Time
{
Integer hour where hour ∈ [0..23];
Integer minute where minute ∈ [0..59];
Integer second where second ∈ [0..59];
}
value unordered DebugTime
{
Integer hour where hour ∈ [0..23];
Integer minute where minute ∈ [0..59];
Integer second where second ∈ [0..59];
}
Implicit ordering
Predica may define an implicit order when the declaration contains enough ordered structure to compare values without adding policy. The default orders are:
- Boolean's
≤relation is the order withfalse ≤ true. - Real's
≤relation is the standard mathematical order. - String's
≤relation is lexicographic order over Unicode scalar values. It is not locale-sensitive collation and does not depend on the string's physical encoding. - A subtype of an ordered type inherits the ordering on its supertype.
Note therefore that the predefined types
Int8,Int16, ...,Float32,Float64, andIntegerinherit an order fromReal. - An attribute of type T is ordered if and only if T is ordered and the cardinality constraint is exactly one. Optional, set-valued, and relation-valued attributes do not acquire an order merely because their element type is ordered.
- A structural type is ordered if and only if each of its attributes is ordered. Structural values are compared lexicographically by the type's attribute sequence, all ascending. Once the type is fixed, the comparison uses the ordered attribute values. The empty structural type is trivially ordered.
- An enumerated type is ordered by default using the source order of its attributes.
- A value type is ordered by default when each base attribute is ordered. Its default order is the structural order of its base attributes in declaration order. Derived attributes are not part of the implicit default order.
- An entity type is ordered by default when its first declared key exists and each key attribute is ordered. Its default order is lexicographic order by the attributes of that key. An entity type without a key has no implicit default order.
- A variant type is ordered by default first by its active attribute discriminant, using the source order of the attributes. If the discriminants are equal, the payloads are compared using the order of the attribute's payload type. Payload-free attributes compare by discriminant alone. An attribute with a multi-valued payload has no implicit payload order.
- A quotient type has no implicit default order. Its values are equivalence classes, not
representation tuples. In general, lexicographic order on the representation fields is
not well-defined on the quotient values: equivalent representatives may have different
positions in the representation order. Therefore it does not generally define a
≤relation satisfying the order axioms on the quotient type.
If no implicit default order is available and no explicit order is supplied, the type is not
ordered. The unordered modifier cannot be combined with an explicit order. Sets remain
mathematical sets; use explicit ordering operations when an ordered list is required.
Ordering value and entity types
An order can be defined explicitly by implementing function Boolean ≤(T x, T y).
Value and entity types can also define a custom order with an order declaration. The order
keys are expressions evaluated in the scope of the value being compared. Keys are compared
lexicographically from left to right.
value Date
{
Integer year;
Month month;
Integer day;
order [year↓, month, day];
}
Order keys can be expressions, not only attribute names:
value Point
{
Real x;
Real y;
order [x*x + y*y, x, y];
}
The direction marker is written after the key expression: ↑ means ascending and ↓ means
descending. If no direction marker is written, ascending order is assumed. The postfix marker
keeps the key expression and the ordering direction separate; for example, year ↓ says to
compare the year key in descending order, not to compare the arithmetic expression -year.
Every order key must be single-valued and ordered. The declared keys must induce a total order
compatible with equality: if all order keys compare equal, the two values must be equal values
of the type. A partial sort key is appropriate for query-level orderBy, but not for the
canonical order of an ordered type.
Ordering an enum type
An order can be defined explicitly on an enum by implementing ≤:
enum Color
{
red,
green,
blue
}
// Explicit order: blue < green < red.
func Boolean ≤(Color x, Color y) =
if x = y then
true
else if x = blue then
true
else if y = blue then
false
else if x = green then
true
else
false;
Ordering a quotient type
A quotient type has no implicit default order, but it can still define ≤ using an expression
whose truth is invariant under the quotient equivalence. For example, rational numbers can be
represented by integer numerator-denominator pairs with positive denominators:
quotient Rational
{
Integer numerator;
Integer denominator where denominator > 0;
equivalence
{
x ~ y where x.numerator * y.denominator = y.numerator * x.denominator;
}
}
The quotient can be embedded in Real by interpreting each rational value as its mathematical
real value:
embed Rational r isa Real =
r.numerator / r.denominator;
This embedding is well-defined on equivalence classes. Because Real is ordered, Rational
inherits Real's standard ≤ relation through the embedding. In terms of the numerator and
denominator representation, the inherited order is equivalent to cross multiplication:
func Boolean ≤(Rational x, Rational y) =
x.numerator * y.denominator ≤ y.numerator * x.denominator;
Because denominators are positive, the cross-multiplication form is invariant under the
equivalence relation: choosing different representatives of the same rational values does not
change the truth value of x ≤ y. It therefore describes a relation on rational values, not
merely on representation tuples, and that relation satisfies the order axioms.