Next: SRFI-11, Previous: SRFI-9, Up: SRFI Support
This SRFI implements a reader extension #,()
called hash-comma.
It allows the reader to give new kinds of objects, for use both in
data and as constants or literals in source code. This feature is
available with
(use-modules (srfi srfi-10))
The new read syntax is of the form
#,(tag arg...)
where tag is a symbol and the args are objects taken as parameters. tags are registered with the following procedure.
Register proc as the constructor for a hash-comma read syntax starting with symbol tag, ie.
#,(
tagarg...)
. proc is called with the given arguments(
procarg...)
and the object it returns is the result of the read.
For example, a syntax giving a list of N copies of an object.
(define-reader-ctor 'repeat (lambda (obj reps) (make-list reps obj))) (display '#,(repeat 99 3)) -| (99 99 99)
Notice the quote '
when the #,( )
is used. The
repeat
handler returns a list and the program must quote to use
it literally, the same as any other list. Ie.
(display '#,(repeat 99 3)) => (display '(99 99 99))
When a handler returns an object which is self-evaluating, like a number or a string, then there's no need for quoting, just as there's no need when giving those directly as literals. For example an addition,
(define-reader-ctor 'sum (lambda (x y) (+ x y))) (display #,(sum 123 456)) -| 579
A typical use for #,()
is to get a read syntax for objects
which don't otherwise have one. For example, the following allows a
hash table to be given literally, with tags and values, ready for fast
lookup.
(define-reader-ctor 'hash (lambda elems (let ((table (make-hash-table))) (for-each (lambda (elem) (apply hash-set! table elem)) elems) table))) (define (animal->family animal) (hash-ref '#,(hash ("tiger" "cat") ("lion" "cat") ("wolf" "dog")) animal)) (animal->family "lion") => "cat"
Or for example the following is a syntax for a compiled regular expression (see Regular Expressions).
(use-modules (ice-9 regex)) (define-reader-ctor 'regexp make-regexp) (define (extract-angs str) (let ((match (regexp-exec '#,(regexp "<([A-Z0-9]+)>") str))) (and match (match:substring match 1)))) (extract-angs "foo <BAR> quux") => "BAR"
#,()
is somewhat similar to define-macro
(see Macros) in that handler code is run to produce a result, but
#,()
operates at the read stage, so it can appear in data for
read
(see Scheme Read), not just in code to be executed.
Because #,()
is handled at read-time it has no direct access
to variables etc. A symbol in the arguments is just a symbol, not a
variable reference. The arguments are essentially constants, though
the handler procedure can use them in any complicated way it might
want.
Once (srfi srfi-10)
has loaded, #,()
is available
globally, there's no need to use (srfi srfi-10)
in later
modules. Similarly the tags registered are global and can be used
anywhere once registered.
There's no attempt to record what previous #,()
forms have
been seen, if two identical forms occur then two calls are made to the
handler procedure. The handler might like to maintain a cache or
similar to avoid making copies of large objects, depending on expected
usage.
In code the best uses of #,()
are generally when there's a
lot of objects of a particular kind as literals or constants. If
there's just a few then some local variables and initializers are
fine, but that becomes tedious and error prone when there's a lot, and
the anonymous and compact syntax of #,()
is much better.