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

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

This page is part of the web mail archives of SRFI 10 from before July 7th, 2015. The new archives for SRFI 10 contain all messages, not just those from before July 7th, 2015.

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