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

Re: What #,(foo) does tell... include vs. load

> 	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)

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.