commit 3599f1025a09752dc7ab02e861820258c157e7aa
parent 25f61802798b65f5f2ce21ff23718685ced19ced
Author: Taylan Ulrich Bayırlı/Kammer <taylanbayirli@gmail.com>
Date: Fri, 14 Aug 2015 17:28:25 +0200
Rewrite README into SRFI format.
Diffstat:
M | README.md | | | 184 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- |
1 file changed, 135 insertions(+), 49 deletions(-)
diff --git a/README.md b/README.md
@@ -1,71 +1,157 @@
-Generic accessor and modifier operators for Scheme
-==================================================
+Generic accessor and modifier operators
+=======================================
-- `(ref object field)` will return the value for `field` in `object`.
-- `(set! object field value)` will set `object`s `field` to `value`.
+Abstract
+--------
-Works with vectors, strings, bytevectors, hashtables, and records.
+Lisp dialects including Scheme have traditionally lacked short,
+simple, generic syntax for accessing and modifying the fields of
+arbitrary "collection" objects. We fill this gap for Scheme by
+defining generalized `ref` and `set!` operators.
-For records, the accepted values for `field` are symbols corresponding
-to the record type's field names.
-`ref` also takes an optional `default` argument. If fields in the
-given object can be "empty" (e.g. a hashtable not having an
-association for a given key), then the default value will be returned
-when the requested field is empty. Otherwise an error is raised.
+Rationale
+---------
+
+In some types of code-bases, accessing and modifying fields of certain
+collection objects (such as vectors, hashtables, or records) are
+ubiquitous operations. Standard Scheme APIs contain verbose procedure
+names specialized for each data type, which may become very tedious to
+type, and clutter the code.
-So, assuming `hashtable` has no entry for `"foo"`:
-- `(ref hashtable "foo" 'default)` => `default`
-- `(ref hashtable "foo")` => error
+In contrast, most other languages offer very short and simple syntax
+for such operations, such as square bracket and dotted notation:
+`object[field]` and `object.field` for access; `object[field] = value`
+and `object.field = value` for modification.
-That's all for starters.
+To accommodate, we define a pair of generic accessor and modifier
+operators that work through type-based dynamic dispatch: `(ref object
+field)` for access and `(set! object field value)` for modification.
+We believe the overhead involved in this is negligible in most
+code-bases, and furthermore a programmer can always fall back to the
+type-specific accessor and modifier procedures in performance-critical
+sections of code.
-Library usage details
----------------------
+The operators are specified to work on bytevectors, hashtables, lists,
+strings, vectors, and all record types. Some notes on specific types:
-One needs to exclude `set!` and `define-record-type` when importing
-`(scheme base)`, and import them from this library instead. (One
-could also import them with different names of course.) Record types
-defined with the standard `default-record-type` won't play along. Of
-course the intent is that Scheme implementations provide these
-extended `set!` and `define-record-type` in their `(scheme base)` so
-this problem won't apply.
+- For bytevectors, 8-bit unsigned integer operations are assumed.
+ There is no obvious way to incorporate other bytevector operations
+ into the generalized API, and a programmer is most likely to have
+ single-byte operations in mind when using a generalized API on
+ bytevectors.
+- For hashtables, the `ref` operator takes an additional `default`
+ argument akin to `hashtable-ref`.
-SRFI-17 and SRFI-105 integration
---------------------------------
+- Lists are supported by testing the given object for a pair. Pairs
+ themselves are senseless to support because `(set! pair car value)`
+ contains the same number of words as `(set-car! pair value)`. In
+ the `ref` equivalent, it contains one word more: `(ref pair car)`.
-`set!` is the setter of `ref` in SRFI-17 terms, so `(set! (ref object
-field) value)` works. This is not useful on its own, but becomes
-useful in combination with SRFI-105. `ref` is also exported as
-`$bracket-apply$`, so e.g. `(set! {object[field]} value)` works. That
-is still not an improvement over the normal `(set! object field
-value)`, however if one also imports e.g. `:=` as a synonym for
-`set!`, one can use some very "C-like" syntax: `{object[field] :=
-value}`.
+- For records, the accepted values for the `field` parameter are
+ symbols corresponding to the record type's field names. The
+ overhead involved in looking up the correct accessor of modifier
+ falls under the same rationale as other kinds of overhead involved
+ with this SRFI.
-I strongly discourage use of that syntax in code which *otherwise*
-doesn't heavily utilize SRFI-105 syntax. The normal `(set! object
-field value)` fits in much more nicely with typical Scheme code.
-However, if you already use SRFI-105 syntax heavily in your code, then
-the `{object[field] := value}` syntax is likely to be a welcome
-addition.
+Alists are unfortunately impossible to support due to the lack of a
+reliable `alist?` predicate. (It's ambiguous in that every alist is
+also a list, and any list may coincidentally have the structure of an
+alist.)
+A `ref*` procedure taking an arbitrary number of `field` arguments and
+walking through several collections was considered, but deemed
+sub-optimal because it doesn't play well with collections that may
+have "empty" fields, and usually one doesn't walk through deep
+structures at once, and instead binds intermediate results to a
+variable. Nevertheless, it is trivial to define if desired:
+
+ (define (ref* object field . fields)
+ (if (null? fields)
+ (ref object field)
+ (apply ref* (ref object field) fields)
+
+This might be a better candidate for SRFI-105's `$bracket-apply$` than
+regular `ref`.
+
+
+Integration with SRFI-17 and SRFI-105
+-------------------------------------
+
+The `set!` operator in this SRFI does not conflict with the one in
+SRFI-17. The reference implementation extends the SRFI-17 `set!` and
+thus supports the functionality of both SRFI-17 and the one described
+here.
+
+Additionally, if SRFI-17 is supported, the `ref` procedure's "setter"
+may be defined as: `(lambda (object field value) (set! object field
+value))`. This is uninteresting in its own right, but can yield an
+interesting combination with SRFI-105. In code that already uses
+SRFI-105 heavily, a programmer may define `$bracket-apply$` as a
+synonym to `ref`, define `:=` as a synonym to `set!`, and then use the
+following syntax: `{object[field] := value}`.
+
+
+Specification
+-------------
+
+- `(ref object field)` (procedure)
+- `(ref object field default)`
+
+Returns the value for `field` in `object`. If `object` is of a type
+whose fields can be "empty" or "unassigned" (e.g. a hashtable), then
+the value of `default` is returned if given, and otherwise an error
+raised.
+
+Valid types for `object` are: bytevectors, hashtables, pairs, strings,
+vectors, and all record types.
+
+Valid types for `field` depend on the type of `object`. For
+bytevectors, hashtables, strings, and vectors, refer to their
+respective `*-ref` procedures. For pairs, refer to `list-ref`. For
+records, symbols that correspond with the record type's field names
+are taken.
+
+If SRFI-17 is supported, then the `ref` procedure has the following
+setter: `(lambda (object field value) (set! object field value))`
+
+- `(set! object field value)` (syntax)
+
+Sets the value for `field` in `object` to `value`.
+
+Valid types for `object` and `field` are the same as in the `ref`
+procedure.
+
+Note: This operator is only a syntax keyword because it overloads the
+normal `set!` syntax.
+
+
+Considerations when using as a library
+--------------------------------------
+
+The intent of this SRFI is to encourage Scheme systems to extend the
+semantics of their default `set!` operator in line with this SRFI. On
+the meanwhile, it can be used as a library, but certain considerations
+apply.
+
+The `set!` and `define-record-type` exports of the library conflict
+with the ones in `(scheme base)`, so either have to be renamed, or
+more typically, the ones from `(scheme base)` excluded.
+
+Record types not defined with the `default-record-type` exported by
+this library won't work with `ref` and `set!`.
-Overhead
---------
-Overhead while defining record types should be generally irrelevant to
-an application (why would you be defining record types during a
-bottleneck?); the overhead inherent to `ref` and `set!` might also
-make them undesirable for the tightest of inner loops, but will surely
-be negligible in the general case.
+Implementation
+--------------
-Using hashtables instead of alists might give a slight improvement.
+A reference implementation as a library is found in the version
+control repository of this SRFI.
Acknowledgments
---------------
-Idea by Jorgen Schäfer AKA "forcer".
+Original idea and some input during design by Jorgen Schäfer.