Syntax-local-introduce and module boundaries

If I use syntax-local-introduce inside one module, then it works:

(define-syntax (require-macro stx)
  (syntax-local-introduce #'(require racket/math)))

(define-syntax (define-macro stx)
  (syntax-local-introduce #'(define x 10)))

(require-macro)
pi
(define-macro)
x

But if I try to use it in another module, it fails:

lib.rkt:
#lang racket/base
(require (for-syntax racket/base))
(provide require-macro define-macro)

(define-syntax (require-macro stx)
  (syntax-local-introduce #'(require racket/math)))

(define-syntax (define-macro stx)
  (syntax-local-introduce #'(define x 10)))

test.rkt:
#lang racket/base

(require "lib.rkt")

(require-macro)
pi ; this returns unbound identifier
(define-macro)
x ; this too

How to make macro introducing definition be usable through require?

1 Like

The problem where is that #'(require racket/math) preserves the lexical context of the place where it occurs, which is not in the module where you wanted the bindings to be visible. The syntax-local-introduce call makes the syntax returned by the macro expansion look like it's not from a macro expansion, but that syntax is still from a different module.

You might want

(define-syntax (require-macro stx)
  (datum->syntax stx '(require racket/math)))

Using datum->syntax with stx makes the result syntax look like it came from the same place as stx, which is the use of the require-macro form.

3 Likes

To nitpick: it should be something like

(define-syntax (require-macro stx)
  (datum->syntax stx (list #'require 'racket/match)))

instead, because you want the local meaning of require (that is, Racket's require form), not whatever require might happen to mean at the macro use site.

3 Likes

This way it works, but breaks DrRacket: I see no arrow to pi and no "imported from" popup.

syntax-local-introduce variant shows "imported from racket/math".

(datum->syntax stx … #’require … stx tsx)
(define-syntax (require-macro stx)
  (datum->syntax stx '(require racket/math) stx stx stx))

and

(define-syntax (require-macro stx)
  (datum->syntax stx (list #'require 'racket/math) stx stx stx))

also don't work with DrRacket.

I've found the correct answer. It is:

(define-syntax (require-macro stx)
  (datum->syntax stx (list #'require (datum->syntax stx 'racket/math stx stx))))

Thank you all for help.

I ran into the same question of 'require' recently. And I searched for it and it brought me here. So it seems to me the (datum->syntax stx ...) is a better choice for this similar cases. Then it came to me, why do we need the syntax-local-introduce? What is the proper usage of it?