在球拍中动态要求1期(语法)变量

时间:2019-04-05 20:09:30

标签: macros racket

假设我有一个模块foo.rkt在阶段1提供了x

#lang racket
(begin-for-syntax
  (define x 5)
  (provide x))

运行(module->exports "foo.rkt")后,您将返回((1 (x ()))),这意味着在阶段1提供了x,并且没有提供其他绑定。

现在,在另一个模块中,我可以在运行时使用x静态导入for-template

#lang racket
(require (for-template "foo.rkt"))
x ; => 5

但这是静态的,因此它将始终发生。

如果这是在阶段0,则可以使用dynamic-require。但是似乎您只能使用dynamic-require运行第1阶段代码,而不能从该运行代码中获取任何值。

还有dynamic-require-for-syntax,但我永远无法工作。

最后,还有namespace-require,但是它将它带到名称空间的阶段1中,而不是阶段0中。所以我可以做类似(eval '(begin-for-syntax (writeln x))的操作,但是只会打印{ {1}},而不是返回它。

还有x,但它似乎也只返回阶段0的值。

那么,无论如何,我是否可以从模块动态(而非静态)导入阶段1变量?

1 个答案:

答案 0 :(得分:2)

是的,有办法,但是有点恶心。

首先,我们需要创建一个基本的名称空间,以便使用(define ns (make-base-namespace))之类的技巧。

接下来,我实际上建议使用namespace-require/expansion-time而不是namespace-require。它将仅实例化模块(也仅运行阶段1代码)。

这样做,x不会导入到名称空间中,而是在阶段1中,因此我们可以编写宏以通过3d语法从阶段1到阶段0“走私”它。

该宏将看起来像:

(eval '(define-syntax (cheater-x stx)
         #`'#,(datum->syntax #f x)))

现在您可以执行(eval 'cheater-x)来获取x的值。

您的总体代码应如下所示:

(define (dynamic-require-from-syntax module binding)
  (define ns (make-base-namespace))
  (parameterize ([current-namespace ns])
    (namespace-require 'racket)
    (namespace-require/expansion-time module)
    (eval `(define-syntax (cheater-x stx)
              #`'#,(datum->syntax #f ,binding)))
    (eval 'cheater-x)))
(dynamic-require-from-syntax "foo.rkt" 'x) ; => 5

很显然,您可以将此函数设置为在多个调用上使用相同的名称空间,这样它就不会在每次调用该模块时重新实例化该模块。但这是一个不同的答案。