在Racket中动态地需要阶段1(for-syntax)变量

问题描述 投票:2回答:1

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

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

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

现在,在另一个模块中,我可以使用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))的事情,但这只会打印x的值,而不是返回它。

还有namespace-variable-value,但它似乎也只是在阶段0返回值。

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

macros racket
1个回答
2
投票

是的,有一种方法,但它有点恶心。

首先,我们需要创建一个基本命名空间,所以像(define ns (make-base-namespace))这样的东西就可以了。

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

这样做,x不会导入到命名空间,但是在阶段1,所以我们可以编写一个宏来将它从第1阶段“走私”到阶段0到3d语法。

宏将看起来像:

(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

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

© www.soinside.com 2019 - 2024. All rights reserved.