Comment 5 for bug 173908

Revision history for this message
Abdulaziz Ghuloum (aghuloum) wrote : Re: [Bug 173908] Re: Macro expansion to (include ...) not working to export bindings inside a library

On Dec 4, 2007, at 11:34 AM, Derick Eddington wrote:

> [...] or there's some mismatch with the syntax-object contextual info,

Right. I'll try to explain it.

The include macro is non-hygienic. It introduces code into the
context in which it was called. So, if you have a file called foo.ss
containing

;;; this is foo.ss

(define foo 12)

;;; end of foo.ss

then you can do the following
(let ()
   (include "foo.ss")
   (+ foo foo))

and this works. Why it works is because include first reads the file
(obtaining a list of s-expressions), then performs a non-hygienic
datum->syntax to add the contextual information. Now datum->syntax
takes an identifier and a datum, where the identifier contains the
actual contextual information. Because the file name is not an
identifier (it's a string), it has no contextual information. So,
include would have to use the first word (i.e. the include keyword)
as the identifier. Include may be defined as:

(define-syntax include
   (lambda (x)
     (syntax-case x ()
       [(ctxt name)
        (cons #'begin
          (with-input-from-file
            (syntax->datum #'name)
            (lambda ()
              (let f ()
                (let ([x (read)])
                  (cond
                    [(eof-object? x) #'()]
                    [else
                     (cons
                       (datum->syntax #'ctxt x)
                       (f))]))))))])))

Now you can write your include-it macro in a similar way, changing
only the part where the file name is determined. And that would make
two broken variations of include.

One solution is to make the name part of the include an identifier
instead of a string. So, you would do (include foo.ss) instead of
(include "foo.ss"). This would kind of work because the context
would be extracted from the identifier foo.ss and not from the
keyword include.

Another option is to define a separate "include-into" macro like:

(define-syntax include-into
   (lambda (x)
     (syntax-case x ()
       [(_ ctxt name)
        --- same code as above ---])))

And define include as:

(define-syntax include
   (lambda (x)
     (syntax-case x ()
       [(ctxt name)
        (include-into ctxt name)])))

Now your code can use "include-into", passing an appropriate context,
and it would work.

BTW: this problem is not limited to include but exists in all other
non-hygienic macros like define-record-type and import. We can
sometimes fake it (like I did in bug 162741). I think the include-
into is a better solution. So, will add.

Aziz,,,