srfi-146

  1. srfi-146
    1. Introduction
    2. Author
    3. Repository
    4. Specification
    5. Linear update
    6. Comparator restrictions
    7. API
      1. Constructors
      2. Predicates
      3. Accessors
      4. Updaters
      5. The whole mapping
      6. Mapping and folding
      7. Copying and conversion
      8. Submappings
      9. Set theory operations
      10. Additional procedures for mappings with ordered keys
      11. Comparators
    8. Examples
    9. License
    10. Version history
      1. 0.1

Introduction

This egg provides the reference implementation of SRFI-146. The (srfi 146) API uses the bundled rbtree implementation whereas the (srfi 146 hash) API relies on the hash-trie egg.

Author

Vasilij Schneidermann

Repository

https://depp.brause.cc/srfi-146

Specification

Mappings form a new type as if created by define-record-type. The effect of using record-type inspection or inheritance for the mapping type is unspecified.

The two mapping types as exported from (srfi 146) and (srfi 146 hash) (see below) are mutually disjoint.

This specification uses the notion of equality of comparators. The exact equality predicate is left implementation-dependent but is always at least as fine as equal? (and not finer than eq?).

It is an error for any procedure defined in this SRFI to be invoked on mappings with distinct comparators, except if noted otherwise.

It is an error to mutate any key while it is contained in an association in a mapping.

It is an error to add any association to a mapping whose key does not satisfy the type test predicate of the comparator.

It is an error to apply any procedures defined in this SRFI whose names end in ! to a mapping while iterating over it.

When part of an R7RS implementation, the library (srfi 146) should export exactly those identifiers that are described in this specification with the appropriate bindings. Should this SRFI become an essential part of a future Scheme system based on R7RS (e.g. R7RS-large), the library's name would become (scheme mapping).

Linear update

The procedures of this SRFI, like those of SRFI 113, by default, are "pure functional" — they do not alter their parameters. However, this SRFI also defines "linear-update" procedures, all of whose names end in !. They have hybrid pure-functional/side-effecting semantics: they are allowed, but not required, to side-effect one of their parameters in order to construct their result. An implementation may legally implement these procedures as pure, side-effect-free functions, or it may implement them using side effects, depending upon the details of what is the most efficient or simple to implement in terms of the underlying representation.

It is an error to reference a (mapping) parameter after a linear-update procedure has been invoked. For example, this is not guaranteed to work:

  (let* ((mapping1
         (mapping (make-default-comparator) 'a 1 'b 2 'c 3)) ; mapping1 = {a↦1,b↦2,c↦3}.
    (mapping2 (mapping-set! mapping1 'd 4))) ; mapping2 = {a↦1,b↦2,c↦3,d↦4}
  mapping1) ; mapping1 = {a↦1,b↦2,c↦3} or mapping1 = {a↦1,b↦2,c↦3;d↦4} ???

However, this is well-defined:

  (let ((mapping1 (mapping (make-default-comparator) 'a 1 'b 2 'c 3)))
    (mapping-set! mapping1 'd 4)) ; ⇒ {a↦1,b↦2,c↦3;d↦4}

So clients of these procedures write in a functional style, but must additionally be sure that, when the procedure is called, there are no other live pointers to the potentially-modified mapping (hence the term "linear update").

There are two benefits to this convention:

In practice, these procedures are most useful for efficiently constructing mappings in a side-effecting manner, in some limited local context, before passing the mapping outside the local construction scope to be used in a functional manner.

Scheme provides no assistance in checking the linearity of the potentially side-effected parameters passed to these functions — there's no linear type checker or run-time mechanism for detecting violations.

Note that if an implementation uses no side effects at all, it is allowed to return existing mappings rather than newly allocated ones, even where this SRFI explicitly says otherwise. For example, mapping-copy could be a no-op.

Comparator restrictions

The library (srfi 146) described in this SRFI requires comparators to provide an ordering predicate. Any implementation shall also provide the library (srfi 146 hash) which implements essentially the same interface (see below) as (srfi 146) but requires comparators to provide a hash function instead of an ordering predicate, unless the equality predicate of the comparator is eq?, eqv?, equal?, string=?, or string-ci=?.

The library (srfi 146 hash) exports exactly the same identifiers (up to a simple prefix-replacing textual substitution as described in the next paragraph), except for those bound to locations which make no sense when an ordering predicate is absent (e.g. mapping-split). Should (srfi 146) once become known as (scheme mapping), it is recommended that the library (srfi 146 hash) receives the alternative name (scheme hashmap).

To make it possible for a program to easily import (srfi 146) and (srfi 146 hash) at the same time, the prefix mapping that prefixes the identifiers in (srfi 146) is substituted by hashmap in (srfi 146 hash). For example, mapping becomes hashmap, mapping? becomes hashmap?, and mapping-ref becomes hashmap-ref. comparator? remains unchanged. For simplicity, the rest of this SRFI refers to the identifiers by their names in (srfi 146).

API

Constructors

[procedure] (mapping comparator arg ...)

Returns a newly allocated mapping. The comparator argument is a SRFI 128 comparator, which is used to control and distinguish the keys of the mapping. The args alternate between keys and values and are used to initialize the mapping. In particular, the number of args has to be even. Earlier associations with equal keys take precedence over later arguments.

[procedure] (mapping-unfold stop? mapper successor seed comparator)

Create a newly allocated mapping as if by mapping using comparator. If the result of applying the predicate stop? to seed is true, return the mapping. Otherwise, apply the procedure mapper to seed. Mapper returns two values which are added to the mapping as the key and the value, respectively. Then get a new seed by applying the procedure successor to seed, and repeat this algorithm. Associations earlier in the list take precedence over those that come later.

Note that the choice of precedence in mapping and mapping-unfold is compatible with mapping-adjoin and alist->mapping detailed below and with alist->hash-table from SRFI 125, but different from the one in mapping-set from below.

Predicates

[procedure] (mapping? obj)

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

[procedure] (mapping-contains? mapping key)

Returns #t if key is the key of an association of mapping and #f otherwise.

[procedure] (mapping-empty? mapping)

Returns #t if mapping has no associations and #f otherwise.

[procedure] (mapping-disjoint? mapping1 mapping2)

Returns #t if mapping1 and mapping2 have no keys in common and #f otherwise.

Accessors

The following three procedures, given a key, return the corresponding value.

[procedure] (mapping-ref mapping key failure success)
[procedure] (mapping-ref mapping key failure)
[procedure] (mapping-ref mapping key)

Extracts the value associated to key in the mapping mapping, invokes the procedure success in tail context on it, and returns its result; if success is not provided, then the value itself is returned. If key is not contained in mapping and failure is supplied, then failure is invoked in tail context on no arguments and its values are returned. Otherwise, it is an error.

[procedure] (mapping-ref/default mapping key default)

Semantically equivalent to, but may be more efficient than, the following code:

  (mapping-ref mapping key (lambda () default))
[procedure] (mapping-key-comparator mapping)

Returns the comparator used to compare the keys of the mapping mapping.

Updaters

[procedure] (mapping-adjoin mapping arg ...)

The mapping-adjoin procedure returns a newly allocated mapping that uses the same comparator as the mapping mapping and contains all the associations of mapping, and in addition new associations by processing the arguments from left to right. The args alternate between keys and values. Whenever there is a previous association for a key, the previous association prevails and the new association is skipped. It is an error to add an association to mapping whose key that does not return #t when passed to the type test procedure of the comparator.

[procedure] (mapping-adjoin! mapping arg ...)

The mapping-adjoin! procedure is the same as mapping-adjoin, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

[procedure] (mapping-set mapping arg ...)

The mapping-set procedure returns a newly allocated mapping that uses the same comparator as the mapping mapping and contains all the associations of mapping, and in addition new associations by processing the arguments from left to right. The args alternate between keys and values. Whenever there is a previous association for a key, it is deleted. It is an error to add an association to mapping whose key that does not return #t when passed to the type test procedure of the comparator.

[procedure] (mapping-set! mapping arg ...)

The mapping-set! procedure is the same as mapping-set, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

[procedure] (mapping-replace mapping key value)

The mapping-replace procedure returns a newly allocated mapping that uses the same comparator as the mapping mapping and contains all the associations of mapping except as follows: If key is equal (in the sense of mapping's comparator) to an existing key of mapping, then the association for that key is omitted and replaced the association defined by the pair key and value. If there is no such key in mapping, then mapping is returned unchanged.

[procedure] (mapping-replace! mapping key value)

The mapping-replace! procedure is the same as mapping-replace, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

[procedure] (mapping-delete mapping key ...)
[procedure] (mapping-delete! mapping key ...)
[procedure] (mapping-delete-all mapping key-list)
[procedure] (mapping-delete-all! mapping key-list)

The mapping-delete procedure returns a newly allocated mapping containing all the associations of the mapping mapping except for any whose keys are equal (in the sense of mapping's comparator) to one or more of the keys. Any key that is not equal to some key of the mapping is ignored.

The mapping-delete! procedure is the same as mapping-delete, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

The mapping-delete-all and mapping-delete-all! procedures are the same as mapping-delete and mapping-delete!, respectively, except that they accept a single argument which is a list of keys whose associations are to be deleted.

[procedure] (mapping-intern mapping key failure)

Extracts the value associated to key in the mapping mapping, and returns mapping and the value as two values. If key is not contained in mapping, failure is invoked on no arguments. The procedure then returns two values, a newly allocated mapping that uses the same comparator as the mapping and contains all the associations of mapping, and in addition a new association mapping key to the result of invoking failure, and the result of invoking failure.

[procedure] (mapping-intern! mapping key failure)

The mapping-intern! procedure is the same as mapping-intern, except that it is permitted to mutate and return the mapping argument as its first value rather than allocating a new mapping.

[procedure] (mapping-update mapping key updater failure success)
[procedure] (mapping-update mapping key updater failure)
[procedure] (mapping-update mapping key updater)

Semantically equivalent to, but may be more efficient than, the following code

  (mapping-set mapping key (updater (mapping-ref mapping key failure success)))

The obvious semantics hold when success (and failure) are omitted (see mapping-ref).

[procedure] (mapping-update! mapping key updater failure success)
[procedure] (mapping-update! mapping key updater failure)
[procedure] (mapping-update! mapping key updater)

The mapping-update! procedure is the same as mapping-update, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

[procedure] (mapping-update/default mapping key updater default)

Semantically equivalent to, but may be more efficient than, the following code

  (mapping-set mapping key (updater (mapping-ref/default mapping key default)))
[procedure] (mapping-update!/default mapping key updater default)

The mapping-update!/default procedure is the same as mapping-update/default, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

[procedure] (mapping-pop mapping failure)
[procedure] (mapping-pop mapping)

The mapping-pop procedure exported from (srfi 146) chooses the association with the least key from mapping and returns three values, a newly allocated mapping that uses the same comparator as mapping and contains all associations of mapping except the chosen one, and the key and the value of the chosen association. If mapping contains no association and failure is supplied, then failure is invoked in tail context on no arguments and its values returned. Otherwise, it is an error.

The hashmap-pop procedure exported by (srfi 146 hash) is similar but chooses an arbitrary association.

[procedure] (mapping-pop! mapping failure)
[procedure] (mapping-pop! mapping)

The mapping-pop! procedure is the same as mapping-pop, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

[procedure] (mapping-search mapping key failure success)

The mapping mapping is searched in order (that is in the order of the stored keys) for an association with key key. If it is not found, then the failure procedure is tail-called with two continuation arguments, insert and ignore, and is expected to tail-call one of them. If an association with key key is found, then the success procedure is tail-called with the matching key of mapping, the associated value, and two continuations, update and remove, and is expected to tail-call one of them.

The hashmap-search procedure exported by (srfi 146 hash) searches in an arbitrary order.

It is an error if the continuation arguments are invoked, but not in tail position in the failure and success procedures. It is also an error if the failure and success procedures return to their implicit continuation without invoking one of their continuation arguments.

The effects of the continuations are as follows (where obj is any Scheme object):

In all cases, two values are returned: the possibly newly allocated mapping and obj.

[procedure] (mapping-search! mapping key failure success)

The mapping-search! procedure is the same as mapping-search, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

The whole mapping

[procedure] (mapping-size mapping)

Returns the number of associations in mapping as an exact integer.

[procedure] (mapping-find predicate mapping failure)

Returns the association with the least key of the mapping mapping consisting of a key and value as two values such that predicate returns a true value when invoked with key and value as arguments, or the result of tail-calling failure with no arguments if there is none. There are no guarantees how many times and with which keys and values predicate is invoked.

The hashmap-find procedure exported by (srfi 146 hash) searches in arbitrary order.

[procedure] (mapping-count predicate mapping)

Returns the number of associations of the mapping mapping that satisfy predicate (in the sense of mapping-find) as an exact integer. There are no guarantees how many times and with which keys and values predicate is invoked.

[procedure] (mapping-any? predicate mapping)

Returns #t if any association of the mapping mapping satisfies predicate (in the sense of mapping-find), or #f otherwise. There are no guarantees how many times and with which keys and values predicate is invoked.

[procedure] (mapping-every? predicate mapping)

Returns #t if every association of the mapping mapping satisfies predicate (in the sense of mapping-find), or #f otherwise. There are no guarantees how many times and with which keys and values predicate is invoked.

[procedure] (mapping-keys mapping)

Returns a newly allocated list of all the keys in increasing order in the mapping mapping.

If hashmap-keys is imported from (srfi 146 hash), the list is returned in arbitrary order.

[procedure] (mapping-values mapping)

Returns a newly allocated list of all the values in increasing order of the keys in the mapping mapping.

If hashmap-values is imported from (srfi 146 hash), the list is returned in arbitrary order.

[procedure] (mapping-entries mapping)

Returns two values, a newly allocated list of all the keys in the mapping mapping, and a newly allocated list of all the values in the mapping mapping in increasing order of the keys.

If hashmap-entries is imported from (srfi 146 hash), the lists are returned in arbitrary, but consistent order.

Mapping and folding

[procedure] (mapping-map proc comparator mapping)

Applies proc, which returns two values, on two arguments, the key and value of each association of mapping in increasing order of the keys and returns a newly allocated mapping that uses the comparator comparator, and which contains the results of the applications inserted as keys and values. Note that this is more akin to set-mapping from SRFI 113 than to hash-table-mapping from SRFI 125. For example:

  (mapping-map (proc (key value)
             (values (symbol->string key) value))
           (make-default-comparator)
           (mapping (make-default-comparator) 'foo 1 'bar 2 'baz 3))
  ; ⇒ (mapping (make-default-comparator) "foo" 1 "bar" 2 "baz" 3)

Note that, when proc defines a mapping that is not 1:1 between the keys, some of the mapped objects may be equivalent in the sense of the comparator's equality predicate, and in this case duplicate associations are omitted as in the mapping constructor. It is unpredictable which one will be preserved in the result.

If hashmap-map is imported from (srfi 146 hash), the associations are mapped in arbitrary order.

[procedure] (mapping-for-each proc mapping)

Invokes proc for every association in the mapping mapping in increasing order of the keys, discarding the returned values, with two arguments: the key of the association and the value of the association. Returns an unspecified value.

If hashmap-for-each is imported from (srfi 146 hash), the associations are processed in arbitrary order.

[procedure] (mapping-fold proc nil mapping)

Invokes proc for each association of the mapping mapping in increasing order of the keys with three arguments: the key of the association, the value of the association, and an accumulated result of the previous invocation. For the first invocation, nil is used as the third argument. Returns the result of the last invocation, or nil if there was no invocation.

If hashmap-fold is imported from (srfi 146 hash), the associations are accumulated in arbitrary order.

[procedure] (mapping-map->list proc mapping)

Calls proc for every association in increasing order of the keys in the mapping mapping with two arguments: the key of the association and the value of the association. The values returned by the invocations of proc are accumulated into a list, which is returned.

If hashmap->list is imported from (srfi 146 hash), the associations are processed in arbitrary order.

[procedure] (mapping-filter predicate mapping)

Returns a newly allocated mapping with the same comparator as the mapping mapping, containing just the associations of mapping that satisfy predicate (in the sense of mapping-find).

[procedure] (mapping-filter! predicate mapping)

A linear update procedure that returns a mapping containing just the associations of mapping that satisfy predicate.

[procedure] (mapping-remove predicate mapping)

Returns a newly allocated mapping with the same comparator as the mapping mapping, containing just the associations of mapping that do not satisfy predicate (in the sense of mapping-find).

[procedure] (mapping-remove! predicate mapping)

A linear update procedure that returns a mapping containing just the associations of mapping that do not satisfy predicate.

[procedure] (mapping-partition predicate mapping)

Returns two values: a newly allocated mapping with the same comparator as the mapping mapping that contains just the associations of mapping that satisfy predicate (in the sense of mapping-find), and another newly allocated mapping, also with the same comparator, that contains just the associations of mapping that do not satisfy predicate.

[procedure] (mapping-partition! predicate mapping)

A linear update procedure that returns two mappings containing the associations of mapping that do and do not, respectively, satisfy predicate.

Copying and conversion

[procedure] (mapping-copy mapping)

Returns a newly allocated mapping containing the associations of the mapping mapping, and using the same comparator.

[procedure] (mapping->alist mapping)

Returns a newly allocated association list containing the associations of the mapping in increasing order of the keys. Each association in the list is a pair whose car is the key and whose cdr is the associated value.

If hashmap->alist is imported from (srfi 146 hash), the association list is in arbitrary order.

[procedure] (alist->mapping comparator alist)

Returns a newly allocated mapping, created as if by mapping using the comparator comparator, that contains the associations in the list, which consist of a pair whose car is the key and whose cdr is the value. Associations earlier in the list take precedence over those that come later.

[procedure] (alist->mapping! mapping alist)

A linear update procedure that returns a mapping that contains the associations of both mapping and alist. Associations in the mapping and those earlier in the list take precedence over those that come later.

Submappings

All predicates in this subsection take a comparator argument, which is a comparator used to compare the values of the associations stored in the mappings. Associations in the mappings are equal if their keys are equal with mappings' key comparator and their values are equal with the given comparator. Two mappings are equal if and only if their associations are equal, respectively.

Note: None of these five predicates produces a total order on mappings. In particular, mapping=?, mapping<?, and mapping>? do not obey the trichotomy law.

[procedure] (mapping=? comparator mapping1 mapping2 ...)

Returns #t if each mapping mapping contains the same associations, and #f otherwise.

Furthermore, it is explicitly not an error if mapping=? is invoked on mappings that do not share the same (key) comparator. In that case, #f is returned.

[procedure] (mapping<? comparator mapping1 mapping2 ...)

Returns #t if the set of associations of each mapping mapping other than the last is a proper subset of the following mapping, and #f otherwise.

[procedure] (mapping>? comparator mapping1 mapping2 ...)

Returns #t if the set of associations of each mapping mapping other than the last is a proper superset of the following mapping, and #f otherwise.

[procedure] (mapping<=? comparator mapping1 mapping2 ...)

Returns #t if the set of associations of each mapping mapping other than the last is a subset of the following mapping, and #f otherwise.

[procedure] (mapping>=? comparator mapping1 mapping2 ...)

Returns #t if the set of associations of each mapping mapping other than the last is a superset of the following mapping, and #f otherwise.

Set theory operations

[procedure] (mapping-union mapping1 mapping2 ...)
[procedure] (mapping-intersection mapping1 mapping2 ...)
[procedure] (mapping-difference mapping1 mapping2 ...)
[procedure] (mapping-xor mapping1 mapping2)

Return a newly allocated mapping whose set of associations is the union, intersection, asymmetric difference, or symmetric difference of the sets of associations of the mappings mappings. Asymmetric difference is extended to more than two mappings by taking the difference between the first mapping and the union of the others. Symmetric difference is not extended beyond two mappings. When comparing associations, only the keys are compared. In case of duplicate keys (in the sense of the mappings comparators), associations in the result mapping are drawn from the first mapping in which they appear.

[procedure] (mapping-union! mapping1 mapping2 ...)
[procedure] (mapping-intersection! mapping1 mapping2 ...)
[procedure] (mapping-difference! mapping1 mapping2 ...)
[procedure] (mapping-xor! mapping1 mapping2)

These procedures are the linear update analogs of the corresponding pure functional procedures above.

Additional procedures for mappings with ordered keys

The following procedures are only exported by the (srfi 146) library and not by (srfi 146 hash):

[procedure] (mapping/ordered comparator arg ...)
[procedure] (mapping-unfold/ordered stop? mapper successor seed comparator)

These are the same as mapping and mapping-unfold, except that it is an error if the keys are not in order, and they may be more efficient.

[procedure] (alist->mapping/ordered comparator alist)
[procedure] (alist->mapping/ordered! mapping alist)

These are the same as alist->mapping and alist->mapping!, except that it is an error if the keys are not in order, and they may be more efficient.

[procedure] (mapping-min-key mapping)
[procedure] (mapping-max-key mapping)

Returns the least/greatest key contained in the mapping mapping. It is an error for mapping to be empty.

[procedure] (mapping-min-value mapping)
[procedure] (mapping-max-value mapping)

Returns the value associated with the least/greatest key contained in the mapping mapping. It is an error for mapping to be empty.

Note: It does not make sense to ask for the least/greatest value contained in mapping because the values have no specified order.

[procedure] (mapping-min-entry mapping)
[procedure] (mapping-max-entry mapping)

Returns the entry associated with the least/greatest key contained in the mapping mapping as two values, the key and its associated value. It is an error for mapping to be empty.

[procedure] (mapping-key-predecessor mapping obj failure)
[procedure] (mapping-key-successor mapping obj failure)

Returns the key contained in the mapping mapping that immediately precedes/succeeds obj in the mapping's order of keys. If no such key is contained in mapping (because obj is the minimum/maximum key, or because mapping is empty), returns the result of tail-calling the thunk failure.

[procedure] (mapping-range= mapping obj)
[procedure] (mapping-range< mapping obj)
[procedure] (mapping-range> mapping obj)
[procedure] (mapping-range<= mapping obj)
[procedure] (mapping-range>= mapping obj)

Returns a mapping containing only the associations of the mapping whose keys are equal to, less than, greater than, less than or equal to, or greater than or equal to obj.

Note: Note that since keys in mappings are unique, mapping-range= returns a mapping with at most one association.

[procedure] (mapping-range=! mapping obj)
[procedure] (mapping-range<! mapping obj)
[procedure] (mapping-range>! mapping obj)
[procedure] (mapping-range<=! mapping obj)
[procedure] (mapping-range>=! mapping obj)

Linear update procedures returning a mapping containing only the associations of the mapping whose keys are equal to, less than, greater than, less than or equal to, or greater than or equal to obj.

[procedure] (mapping-split mapping obj)

Returns five values, equivalent to the results of invoking (mapping-range< mapping obj), (mapping-range<= mapping obj), (mapping-range= mapping obj), (mapping-range>= mapping obj), and (mapping-range> mapping obj), but may be more efficient.

[procedure] (mapping-split! mapping obj)

The mapping-split! procedure is the same as mapping-split, except that it is permitted to mutate and return the mapping rather than allocating a new mapping.

[procedure] (mapping-catenate comparator mapping1 key value mapping2)

Returns a newly allocated mapping using the comparator comparator whose set of associations is the union of the sets of associations of the mapping mapping, the association mapping key to value, and the associations of mapping2. It is an error if the keys contained in mapping1 in their natural order, the key key, and the keys contained in mapping2 in their natural order (in that order) do not form a strictly monotone sequence with respect to the ordering of comparator.

[procedure] (mapping-catenate! mapping1 key value mapping2)

The mapping-catenate! procedure is the same as mapping-catenate, except that it is permitted to mutate and return one of the mappings rather than allocating a new mapping.

[procedure] (mapping-map/monotone proc comparator mapping)

Equivalent to (mapping-map proc comparator mapping), but it is an error if proc does not induce a strictly monotone mapping between the keys with respect to the ordering of the comparator of mapping and the ordering of comparator. Maybe be implemented more efficiently than mapping-map.

[procedure] (mapping-map/monotone! proc comparator mapping)

The mapping-map/monotone! procedure is the same as mapping-map/monotone, except that it is permitted to mutate and return the mapping argument rather than allocating a new mapping.

[procedure] (mapping-fold/reverse proc nil mapping)

Equivalent to (mapping-fold proc nil mapping) except that the associations are processed in reverse order with respect to the natural ordering of the keys.

Comparators

[procedure] (comparator? obj)

Type predicate for comparators as exported by (srfi 128).

Rationale: The reason why comparator? is reexported from (srfi 128) is that it helps to detect if an implementation of the R7RS module system is broken in the sense that it does not allow interdependent libraries. If a program's imports are (import (srfi 128) (srfi 146)), it would be an error (principally detectable at expansion time) if the Scheme system loaded (srfi 128) twice, once for importing into the program, and once for importing into (srfi 146). Namely, in that case the main program would import comparator? with two different bindings, and it would be impossible to invoke any procedure of (srfi 146) with a comparator created in the top-level program.

If (srfi 146) didn't export comparator?, multiple loadings of (srfi 128) would not be detected early; only when a (srfi 146) procedure is invoked with a comparator created in the top-level program.

One may view exporting comparator? as a hack only necessary because the R7RS library system fails to say anything about interdependent libraries (although it is usually assumed that interdependent libraries are possible); one can, however, also make independent sense out of it: By exporting comparator?, this specification declares or announces the actual type of comparators, on which its procedures depend.

[procedure] (make-mapping-comparator comparator)

Returns a comparator for mappings that is compatible with the equality predicate (mapping=? comparator mapping1 mapping2). If make-mapping-comparator is imported from (srfi 146), it provides a (partial) ordering predicate that is applicable to pairs of mappings with the same (key) comparator. If (make-hashmap-comparator) is imported from (srfi 146 hash), it provides an implementation-dependent hash function.

If make-mapping-comparator is imported from (srfi 146), the lexicographic ordering with respect to the keys (and, in case a tiebreak is necessary, with respect to the ordering of the values) is used for mappings sharing a comparator.

The existence of comparators returned by make-mapping-comparator allows mappings whose keys are mappings themselves, and it allows to compare mappings whose values are mappings.

The following comparator is used to compare mappings when (make-default-comparator) from SRFI 128 is invoked:

mapping-comparator

mapping-comparator is constructed by invoking make-mapping-comparator on (make-default-comparator).

Examples

(import scheme)
(import (srfi 128))
(import (srfi 146))
(import (srfi 146 hash))

(define comparator (make-default-comparator))

(define m (mapping comparator 'c 3 'e 5))
(set! m (mapping-set m 'b 2))
(set! m (mapping-set m 'd 4))
(set! m (mapping-set m 'a 1))
(mapping-for-each (lambda (k v) (print (list k v))) m)
;; (a 1)
;; (b 2)
;; (c 3)
;; (d 4)
;; (e 5)

(define ht (hashmap comparator 'c 3 'e 5))
(set! ht (hashmap-set ht 'b 2))
(set! ht (hashmap-set ht 'd 4))
(set! ht (hashmap-set ht 'a 1))
(hashmap-for-each (lambda (k v) (print (list k v))) ht)
;; (e 5)
;; (d 4)
;; (c 3)
;; (b 2)
;; (a 1)

License

Copyright (c) 2020, Vasilij Schneidermann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Version history

0.1