Ord extends Eq, and therefore requires an implementation for both
[Eq.eq] and [Ord.cmp].
The most common implementation strategies are writing classes and patching
existing prototypes. Implementation is implicit and does not require an
implements clause. TypeScript uses structural subtyping to determine
whether a value implements Ord.
Working with generic types requires additional consideration: in some cases,
a generic type implements Ordonly when one or more of its generic
parameters implement Ord; in these cases, we must require an Ord
implementation from the parameter(s). In other cases, there are no such
requirements.
Implementing [Eq.eq] for a type that has already implemented [Ord.cmp] is
trivial: simply check whether the result of comparing two instances is the
Equal variant of Ordering. Sometimes, determining equality can be done in
a more efficient manner without relying on [Ord.cmp].
Existing types can be patched to implement Ord. This strategy works well
for types that:
are built-in or imported from external modules.
do not provide access to their implementation.
have a single, specific behavior as a total order, or where the
programmer wishes to implement a default behavior.
Patching a type in TypeScript requires two steps:
an augmentation for a module or the global scope that patches the
type-level representation; and
a concrete implementation for [Ord.cmp] and [Eq.eq].
The concrete implementation logic is similar to writing a method body for a
class or object, and the same practices apply for requiring generic
parameters to implement Ord.
Example
Non-generic implementation
Consider a Book type that determines ordering by comparing ISBNs:
In this example, books are ordered first by their ISBN, and then by their
format. Notice how the semigroup behavior of Ordering along with the cmb
function is used here to combine two Ordering.
Example
Generic implementation with no Ord requirements
Consider a type that orders arrays by comparing their lengths:
Notice the extra syntax when implementing [Ord.cmp]. We introduce a
method-scoped generic parameter T and require that it has an Ord
implementation by writing T extends Ord<T> (the name T is arbitrary).
Then, we require that this and that are Arr<T> where T extends Ord<T>. This allows us to use icmp to implement our desired behavior.
Example
Generic implementation with multiple Ord requirements
Consider a Pair type that orders two distinct values lexicographically,
which requires that each value is an implementor of Ord:
The syntax is similar to the Arr implementation above. Notice there are now
two method-scoped generic parameters that are each required to implement
Ord.
Example
Non-generic augmentation
Consider a module augmentation for an externally defined Book type:
An interface that provides evidence of a total order.
Remarks
Properties
In addition to being equivalence relations, implementors of
Ord
must define a method of comparison such that an implementation is also:le(x, x)
;le(x, y)
andle(y, x)
implieseq(x, y)
;le(x, y)
andle(y, z)
impliesle(x, z)
; andle(x, y)
orle(y, x)
for all values
x
,y
, andz
.Implementing
Ord
Ord
extendsEq
, and therefore requires an implementation for both[Eq.eq]
and[Ord.cmp]
.The most common implementation strategies are writing classes and patching existing prototypes. Implementation is implicit and does not require an
implements
clause. TypeScript uses structural subtyping to determine whether a value implementsOrd
.Conditional implementation
Working with generic types requires additional consideration: in some cases, a generic type implements
Ord
only when one or more of its generic parameters implementOrd
; in these cases, we must require anOrd
implementation from the parameter(s). In other cases, there are no such requirements.Deriving
[Eq.eq]
from[Ord.cmp]
Implementing
[Eq.eq]
for a type that has already implemented[Ord.cmp]
is trivial: simply check whether the result of comparing two instances is theEqual
variant ofOrdering
. Sometimes, determining equality can be done in a more efficient manner without relying on[Ord.cmp]
.Writing classes
Classes and objects can implement
Ord
. This strategy works best for types that:Additionally, classes can easily wrap existing types to provide a variety of
Ord
implementations. These "helper" classes are useful for types that:Ord
but can have alternative implementations.Patching existing prototypes
Existing types can be patched to implement
Ord
. This strategy works well for types that:Patching a type in TypeScript requires two steps:
[Ord.cmp]
and[Eq.eq]
.The concrete implementation logic is similar to writing a method body for a class or object, and the same practices apply for requiring generic parameters to implement
Ord
.Example
Non-generic implementation
Consider a
Book
type that determines ordering by comparing ISBNs:If desired, we can also consider the book format when ordering two books:
In this example, books are ordered first by their ISBN, and then by their format. Notice how the semigroup behavior of
Ordering
along with thecmb
function is used here to combine twoOrdering
.Example
Generic implementation with no
Ord
requirementsConsider a type that orders arrays by comparing their lengths:
Notice how
Len
is generic, but there are no special requirements for implementing[Ord.cmp]
.Example
Generic implementation with an
Ord
requirementConsider a type that orders arrays by comparing their elements lexicographically, which requires that the elements implement
Ord
:Notice the extra syntax when implementing
[Ord.cmp]
. We introduce a method-scoped generic parameterT
and require that it has anOrd
implementation by writingT extends Ord<T>
(the nameT
is arbitrary).Then, we require that
this
andthat
areArr<T>
whereT extends Ord<T>
. This allows us to useicmp
to implement our desired behavior.Example
Generic implementation with multiple
Ord
requirementsConsider a
Pair
type that orders two distinct values lexicographically, which requires that each value is an implementor ofOrd
:The syntax is similar to the
Arr
implementation above. Notice there are now two method-scoped generic parameters that are each required to implementOrd
.Example
Non-generic augmentation
Consider a module augmentation for an externally defined
Book
type:Example
Generic augmentation
Consider a global augmentation for the
Array
prototype: