[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

define-reader-ctor is merely an implementation hint

> The only way to supply contructor functions in SRFI-10 is by doing
> (define-reader-ctor <symbol> <proc>).

No. The only requirement SRFI-10 mandates is that "There must be a way
to declare an association between a symbolic tag and the corresponding
constructor-procedure." SRFI-10 goes on to _suggest_ that an
implementation may want to implement a define-reader-ctor function for
that purpose. SRFI-10 does not say that define-reader-ctor is the
required method to build tag-constructor associations, let alone that
it is the only method (see also below).

> The reader is used in two different ways: it is used to read
> the original program and it is used when that program calls
> `read'.  But how can a program modify the reader that was used
> to read it?

<blockquote cite="SRFI-10#last-but-two-paragraph">
The remaining question is how and when to define
reader-constructors. If define-reader-ctor is a regular function,
declaration of a new reader-constructor will affect the Scheme reader
only when the code has been completely read, (byte)compiled and is
being executed. We would like to be able to extend the Scheme reader
while it still reads the code. Fortunately, there are several ways to
affect a compiler/interpreter while it scans the code. For example,
define-macro or define-syntax "extend" a Scheme compiler at compile
time. So do command-line switches and pragmas, which are present in
many systems. In addition, Scheme implementations often permit
customization via profile forms, which may also be specified on
command line.

Here is one example how this can be accomplished. Let's assume we have
a Scheme-to-C compiler scc, which supports R5RS and SRFI-10. Let's
assume that this compiler is extensible: it is capable of loading
"shared objects". Let us assume that srfi-4.so is a shared object that
implements uniform vectors; among other procedures, it defines
f32vector constructor, and adds association between 'f32 and this
constructor. This shared object also defines a "feature" srfi-4 (per
SRFI-0). If I have a Scheme source code file containing

(define (temp-proc)
     (let ((v '#,(f32 1.0 2.0 3.0))) (f32vector-ref v 1)))

I can compile it as
	scc -e srfi-4.so filename.scm

srfi-4.so may originally be written in Scheme, C, assembly etc.
The source language for f32vector constructor is irrelevant, as long
as it can take Scheme values and return a Scheme value, in a manner
consistent with the rest of the compiler.

This scenario isn't that hypothetical as it sounds. Gambit Scheme
Compiler and interpreter permit a user specify pieces of code to be
executed at compiler's start-up time. These pieces can be given in
various profile files, or on the command line. The following excerpt
shows how to modify a Gambit Scheme interpreter before it started
evaluating user's code. The modification includes enabling SRFI-10,
SRFI-0, and building a list of features.

                ; Interpret file file-name given feature-list
(define (with-features feature-list file-name)
  (cerr "\n\twhen features " feature-list " are defined: ")
  (OS:system "gsi -e '(##include \"cond-expand.scm\")' "
    "-e \"(define ALL-FEATURES '"
    (with-output-to-string (lambda () (write feature-list)))
    ")\" " file-name))

BTW, the Gambit Scheme system also permits loading of shared objects,
for example, at start-up time. Making a shared object is such a hassle

	I admit SRFI-10's normative silence about precise ways to
define reader-constructor associations may sound like a cop-out. Yet
is is intentional: the purpose of SRFI-10 is to extend the _protocol_
by which a Scheme system communicates with an external world. If my
and your applications run by a SRFI-10-compliant system, and I send
you a string
	"#,(f32 1.0 2.0 3.0)"
then you know what I am trying to say. What you're going to do about
this -- reject or accept my request for constructing a value -- is
another matter entirely.

	I don't think SRFI-10 is way out of the line in this
respect. For example, SRFI-0 talks about feature identifiers -- yet it
gives no details how an implementation happens to have these
identifiers defined, and how a user may extend the system with more
identifiers (if he can at all).

>  (make-reader)   -> <reader>
>  (extend-reader <reader> <symbol> <proc>) -> <reader>
> A <reader> is function of one argument, an input port, that
> reads and returns the next s-expression from the port.
>  (<reader> <input-port>) -> <s-expression>
> `Make-reader' returns a reader that parses the R5RS external
> syntax.  `Extend-reader' returns a new reader that is the same
> as its first argument except that it uses <proc> as a constructor
> when it finds #,(<symbol> . <args>) in the input, as is done
> in SRFI-10.

And thus you have outlined another implementation of SRFI-10. BTW, the
reference implementation is not that different. Gambit's reader is
written in R4RS Scheme. I extend it _slightly_ to handle a #,() form.
Instead of (make-reader) procedure I rely on a _readtable_ (which
Gambit supports). Readtables also offer modularity, and a possibility
to have multiple different readers in a program. Again, the reader and
my extension are written entirely in R4RS, and support a superset of
R4RS (including SRFI-10).

> It has the added advantages of being modular ... and being easy to
> implement in R5RS.

I'm afraid I'm a bit confused by mentioning of ease of implementation
in R5RS. A R5RS-compliant system can certainly be implemented in R4RS
Scheme. That does not mean that R5RS is somehow redundant.