[plt-scheme] (require... ) expanded from a macro

From: Jens Axel Søgaard (jensaxel at soegaard.net)
Date: Sun Oct 22 07:01:38 EDT 2006

Matthew Flatt skrev:
> At Sat, 21 Oct 2006 15:52:44 +0200, Jens Axel Søgaard wrote:
>> To break hygiene use syntax-local-introduce:
>>    (define-syntax (require-extension stx)
>>      (syntax-case stx (srfi)
>>        ((_ (srfi n))
>>         (number? (syntax-e #'n))
>>         (let ([n (syntax-e #'n)])
>>           (with-syntax ([name (format "~a.ss" n)])
>>             (syntax-local-introduce
>>              #'(require (lib name "srfi"))))))))
> In this case, I recommend `datum->syntax-object', instead:
>    (define-syntax (require-extension stx)
>      (syntax-case stx (srfi)
>        ((_ (srfi n))
>         (number? (syntax-e #'n))
>         (with-syntax ([path (datum->syntax-object
>                              #'n
>                              `(lib ,(format "~a.ss" (syntax-e #'n)) 
>                                    "srfi"))])
>            #'(require path)))))
> The `datum->syntax-object' function is more standard and easier to
> reason about. The above use makes `(lib "n.ss" "srfi")' have the same
> lexical context as the original `n', instead of the context of
> macro-introduced syntax. That way, the bindings introduced by `(lib
> "n.ss" "srfi")' will bind in the same context where `n' would have
> bound.

I hope both macros accomplish that.

> `syntax-local-introduce' is a low-level feature that I still only know
> how explain in operational terms (i.e., in terms of the "marks" and
> "renamings" used for syntax-case).

Admitted, I do tend to think of it in terms of marks.

However in the special case where syntax-local-introduce
is applied to a syntax-object constructed by the transformer
containing no parts of the original syntax, I think of it
as "introduce this syntax-object into context of the macro


   (define-syntax foo
     (lambda (stx)
           #'(require (lib "42." "srfi"))))

   (define-syntax foo
     (lambda (stx)
          stx '(require (lib "42." "srfi"))))

is equivalent (except for the source location info).

To avoid trouble I avoid using syntax-local-introduce on
syntax-objects containing parts of the original syntax.

The main appeal for me, I think, is that "the context of the macro 
application" is implicit. It is quite common to see
datum->syntax-object used with the syntax-object representing
the macro application. Also the "introduce" part of the name
syntax-local-introduce indicates the intention better than 
datum->syntax-object, which has many uses besides introducing
identifers in the original context.

It is probably a matter of taste, whether it worthwhile
to have a syntax-introduce-identifier.

     (syntax-introduce-identifier id-stx)
   ~ (datum->syntax-object orig-stx id-stx)

     (syntax-introduce-identifier id-stx src-loc-stx)
   ~ (datum->syntax-object orig-stx id-stx src-loc-stx)

[Btw - what is the easiest way to transfer the source-location
        from src-loc-stx to id-stx?]

     (syntax-introduce-identifier id-stx src-loc prop-stx)
   ~ (datum->syntax-object orig-stx id-stx src-loc prop-stx)

 > Looking at other uses of `syntax-local-introduce' in our code base ...

Another use is preventing additional marking, when a macro embeds return
values of syntax-local-get-shadower in its output.

But syntax-local-get-shadower is rare:


> In short, use `syntax-local-introduce' when you are cooperating closely
> with the macro expander or managing the macro-expansions of a
> sub-language. I think you should probably avoid it, otherwise.

Sound advice. Low level features ought to be avoided.

> I'm not certain that I've given this advice consistently, in part
> because I'm not entirely sure of the uses myself. Indeed, I'd prefer to
> get rid of `syntax-local-introduce', but something better seems to be
> missing in our syntax system, so that `syntax-local-introduce'
> sometimes seems necessary.

Food for thought.

Jens Axel Søgaard

Posted on the users mailing list.