This site is a static rendering of the Trac instance that was used by R7RS-WG1 for its work on R7RS-small (PDF), which was ratified in 2013. For more information, see Home. For a version of this page that may be more recent, see ComparatorsCowan in WG2's repo for R7RS-large.

Comparators­Cowan

cowan
2015-10-23 22:13:02
20history
source

Please see SRFI 114 for the predecessor to this proposal. SRFI 114 has been deprecated by its author. Here is the first draft of a simpler and better replacement, which will eventually become a new SRFI.

Abstract

This SRFI provides comparators, procedure bundles that represent one or more of an equality predicate, an ordering predicate, and a hash function. By packaging these procedures together, along with a type test predicate, they can be treated as a single item for use in the implementation of data structures.

Issues

None at present.

Rationale

The four procedures above have complex dependencies on one another, and it is inconvenient to have to pass them individually to other procedures that might or might not make use of all of them. For example, a set implementation by nature requires only an equality predicate, but if it is implemented using a hash table, an appropriate hash function is also required if the implementation does not provide one; alternatively, if it is implemented using a tree, an ordering predicate is required. By passing a comparator rather than a bare equality predicate, the set implementation can make use of whatever procedures are available and useful to it.

This SRFI is a simplified and enhanced rewrite of SRFI 114, and shares some of its design rationale and all of its acknowledgements. Special thanks to Taylan Ulrich Bayırlı/Kammer, whose insistence that SRFI 114 was over-complicated but also inadequate inspired this redesign.

Specification

The procedures in this SRFI are in the (srfi ???) library (or (srfi :???) on R6RS), but the sample implementation currently places them in the (comparators) library. This means it can't be used alongside SRFI 114, but there's no reason for anyone to do that.

Definitions

A comparator is an object of a disjoint type. It is a bundle of procedures that are useful for comparing two objects either for equality or for ordering. There are four procedures in the bundle:

It is also the programmer's responsibility to ensure that all four procedures provide the same result whenever they are applied to the same object(s) (in the sense of eqv?), unless the object(s) have been mutated since the last invocation. In particular, they must not depend in any way on memory addresses in implementations where the garbage collector can move objects in memory.

Limitations

The comparator objects defined in this SRFI are not applicable to circular structure or to NaNs or objects containing them. Attempts to pass any such objects to any procedure defined here, or to any procedure that is part of a comparator defined here, is an error except as otherwise noted.

Index

Predicates

(comparator? obj)

Returns #t if obj is a comparator, and #f otherwise.

(comparator-ordered? comparator)

Returns #t if comparator has a supplied ordering predicate, and #f otherwise.

(comparator-hashable? comparator)

Returns #t if comparator has a supplied hash function, and #f otherwise.

Constructors

The following comparator constructors all supply appropriate type test functions, equality predicates, ordering predicates, and hash functions based on the supplied arguments. They are allowed to cache their results: they need not return a newly allocated object, since comparators are pure and functional.

(make-comparator type-test equality ordering hash)

Returns a comparator which bundles the type-test, equality, ordering, and hash procedures provided. As a convenience, the following additional values are accepted:

Here are calls on make-comparator that will return useful comparators for standard Scheme types:

(make-pair-comparator car-comparator cdr-comparator)

This comparator compares pairs using a default comparator (see below) on their cars. If the cars are not equal, that value is returned. If they are equal, the default comparator is used on their cdrs and that value is returned.

(make-list-comparator [ element-comparator [ type-test [ empty? head tail ] ] ])

Returns a comparator which compares two sequences that satisfy type-test lexicographically, as follows:

  • The empty sequence, as determined by calling empty? (null? by default) compares equal to itself.
  • The empty sequence compares less than any non-empty sequence.
  • Two non-empty sequences are compared by comparing the result of calling the head procedure (car by default). If the heads are not equal when compared using element-comparator (a default comparator by default), then the result is the result of that comparison. Otherwise, the results of calling the tail procedure (cdr by default) are compared recursively.

    In particular, (make-list-comparator) returns a comparator that compares lists using a default comparator for the elements, and (make-list-comparator element-comparator) returns a comparator that compares lists using element-comparator for the elements.

    (make-vector-comparator [ element-comparator [ type-test [ length ref ] ] ])

    Returns a comparator which compares two sequences that satisfy type-test as vectors, using the length procedure (vector-length by default) to determine the lengths of the sequences, and the ref procedure (vector-ref by default) to access a particular elements. If one sequence is shorter than the other, it compares less than the other. If the sequences are the same length, they are compared element-wise using element-comparator (a default comparator by default) until they are exhausted.

    In particular, (make-vector-comparator returns a comparator that compares vectors using a default comparator for the elements, and (make-vector-comparator element-comparator) returns a comparator that compares vectors using element-comparator. Similarly, (make-vector-comparator (make-default-comparator) bytevector? bytevector-length bytevector-u8-ref) returns a comparator that compares bytevectors.

    (make-eq-comparator)

    (make-eqv-comparator)

    (make-equal-comparator)

    The equality predicates of these comparators are eq?, eqv?, and equal? respectively. When their ordering procedures are applied to non-equal objects, their behavior is implementation-defined. The type test predicates always return #t.

    These comparators accept circular structure (in the case of equal-comparator, provided the implementation's equal? predicate does so) and NaNs.

    Standard hash functions

    These are hash functions for standard Scheme types, suitable for passing to make-comparator.

    (make-boolean-hash)

    (make-char-hash)

    (make-char-ci-hash)

    (make-string-hash)

    (make-string-ci-hash)

    (make-symbol-hash)

    (make-number-hash)

    Return suitable hash functions for the specified types. Each hash function has the signature (hash-function object [ bound [ salt ] ] ). The hash functions returned by make-char-ci-hash and make-string-ci-hash treat their first argument case-insensitively.

    Default comparators

    (make-default-comparator)

    Returns a comparator known as a default comparator that accepts any two Scheme values (with the exceptions listed in the Limitations section) and orders them in some implementation-defined way, subject to the following conditions:

    • The following ordering between types must hold: the empty list precedes pairs, which precede booleans, which precede characters, which precede strings, which precede symbols, which precede numbers, which precede vectors, which precede bytevectors, which precede all other objects.

    • When applied to pairs, it must behave the same as a comparator returned by make-pair-comparator when applied to no arguments.

    • When applied to booleans, it must compare them using the total order #f < #t and hash them using the hash function returned by make-boolean-hash.

    • When applied to characters, it must compare them using char=? and char<?. In R5RS, this is an implementation-dependent order that is typically the same as Unicode codepoint order; in R6RS and R7RS, it is Unicode codepoint order. It must also hash them using the hash function returned by make-char-hash.

    • When applied to strings, it must compare them using string=? and string<?. In R5RS, this is lexicographic order on the implementation-dependent order defined by char<?; in R6RS it is lexicographic order order on Unicode codepoint order; in R7RS it is an implementation-defined order. It must also hash them using the hash function returned by make-string-hash.

    • When applied to symbols, it must compare them using an implementation-dependent total order. One possibility is to use the order obtained applying symbol->string to the symbols and comparing them using the total order implied by string<?. It must also hash them using the hash function returned by make-symbol-hash. It is not a requirement that symbol hash functions be consistent with string hash functions, however.

    • When applied to numbers where either number is complex, since non-real numbers cannot be compared with <, the following least-surprising ordering is defined: If the real parts are < or >, so are the numbers; otherwise, the numbers are ordered by their imaginary parts. This can still produce somewhat surprising results if one real part is exact and the other is inexact. It must also hash them using the hash function returned by make-number-hash.

    • When applied to real numbers, it must compare them using = and <, and hash them using the hash function returned by make-number-hash.

    • When applied to vectors, it must behave the same as a comparator returned by make-vector-comparator when applied to no arguments.

    • When applied to bytevectors, it must behave the same as that of a comparator created by (make-vector-comparator (make-default-comparator) bytevector? bytevector-length bytevector-u8-ref).

    • When applied to types registered with comparator-register-default!, it must behave the same as the comparator registered using that function.

    • Given disjoint types a and b, one of three conditions must hold:

      • All objects of type a compare less than all objects of type b.
      • All objects of type a compare greater than all objects of type b.
      • All objects of either type a or type b compare equal to each other. This is not permitted for any of the standard types listed above.

    (comparator-register-default! comparator)

    Registers comparator for use by default comparators, such that if the objects being compared both satisfy the type test predicate of comparator, it will be employed by default comparators to compare them.

    Accessors

    (comparator-type-test-procedure comparator)

    Returns the type test predicate of comparator.

    (comparator-equality-predicate comparator)

    Returns the equality predicate of comparator.

    (comparator-ordering-predicate comparator)

    Returns the ordering predicate of comparator.

    (comparator-hash-function comparator)

    Returns the hash function of comparator.

    (comparator-check-type comparator obj)

    Invokes the type test predicate of comparator on obj and returns true if it returns true and signals an error otherwise.

    Comparison predicates

    (=? comparator object1 object2 object3 ...)

    (<? comparator object1 object2 object3 ...)

    (>? comparator object1 object2 object3 ...)

    (<=? comparator object1 object2 object3 ...)

    (>=? comparator object1 object2 object3 ...)

    These procedures are analogous to the number, character, and string comparison predicates of Scheme. They allow the convenient use of comparators to handle arbitrary data types.

    These procedures apply the comparison procedure of comparator to the objects as follows. If the specified relation returns #t for all objecti and objectj where n is the number of objects and 1 <= i < j <= n, then the procedures return #t, but otherwise #f.

    The order in which the values are compared is unspecified. Because the relations are transitive, it suffices to compare each object with its successor.

    Implementation

    The sample implementation contains the following files: FIXME

    • basics.scm - the syntax, record type definition, and simple constructors and procedures
    • default.scm - a simple implementation of the default constructor, which should be improved by implementers to handle records and implementation-specific types
    • constructors.scm - most of the constructors
    • advanced.scm - the more complex constructors
    • r7rs-shim.scm - procedures for R7RS compatibility, including a trivial implementation of bytevectors on top of SRFI 4 u8vectors
    • complex-shim.scm - a trivial implementation of real-part and imag-part for Schemes that don't have complex numbers
    • comparators.sld - an R7RS library
    • comparators.scm - a Chicken library

    A future release will include a test program using the Chicken test egg, which is available on Chibi as the (chibi test) library.