Re: What #,(foo) does tell... include vs. load
oleg@xxxxxx 07 Oct 1999 19:04 UTC
> Now consider
> (cond-expand
> ((and srfi-4 srfi-10 srfi-10-4)
> (define sample-vector '#,(f32 1.0 2.0 3.0))))
> Note that mixing "cond-expand" and "#,(...)" as in this example is not
> a good practice. That is because "#," operates at read-time and
> cond-expand operates later, at macro-expansion time. So the
> constructor for f32 will be executed regardless of the presence of
> srfi-4, srfi-10, and srfi-10-4 (and if "#," is not supported by the
> reader, you will get a reader error).
You're absolutely right. I'm afraid I got carried away with the
example and overlooked the timing problem. Sorry.
I'd like to note however that according to R5RS conventions, a
phrase "It is also an error if a reader-constructor associated with
the tag cannot be located" does not necessarily mean that a read-time
application error should crash the program and generate a core
dump. An implementation may choose to treat a #,(foo arg...) form for
an unknown tag foo as being equivalent to #f, the result of (if #f #f)
or a form (error "read-time tag foo is unknown"). The latter two
choices report an error lazily, shifting the burden of dealing with
the error to another phase. A macro-expansion may however re-write the
expression so that the erroneous form disappears. Therefore, there
exists an implementation that can handle the example above as it
stands. Alas, SRFI-10 reference implementation does not do that.
> The "#," mechanism requires the user to understand yet another level
> of compilation and the time when it is performed (and the model is
> already not that simple if you consider forms like "(load ...)",
> "(include ...)", and "(eval ...)", and the REPL).
That's an excellent point. Let's indeed consider load and include forms.
Provided that the following function is defined
(define (read-all-as-a-begin-expr filename)
(with-input-from-file filename
(lambda ()
(cons 'begin
(let loop ((datum (read)))
(if (eof-object? datum) '()
(cons datum (loop (read)))))))))
and an 'include' reader-ctor is set up as
(define-reader-ctor 'include read-all-as-a-begin-expr)
The following form
#,(include "foo.scm")
appears to have the same semantics as
(include "foo.scm")
in Gambit. Furthermore, the load form can be defined as
(define (load filename)
(eval (read-all-as-a-begin-expr filename)
(interaction-environment)))
IMHO this illustrates rather well the difference and similarities
between the include and load forms.
As a matter of fact, this is almost literally how the include and
load forms are implemented in Gambit, as a file gambc30/lib/_eval.scm
reveals. Both forms rely on a ##read-all-as-a-begin-expr-from-port
procedure, which reads from a port rather than a file.