我知道我在这里要问的一般是邪恶的。我特别要求,因为我想进行测试以确保我的静态分析正常工作,即使用户确实调用了eval(这个问题是关于什么)。要了解更多信息,请查看On eval in dynamic languages generally and in Racket specifically
在许多其他动态语言中,我可以使用eval
来修改当前环境的状态。我想在Racket中做类似的事情。例如,我想写一些类似的东西:
#lang racket
(define x 5)
(eval '(set! x 6))
(displayln x)
将x
设置为6
。在Racket中可以这样做吗?
答案 0 :(得分:3)
这可以使用命名空间锚点。具体来说,您需要define-namespace-anchor
和namespace-anchor->namespace
。
具体来说,您在要绑定语法对象的代码中使用define-namespace-anchor
。并使用namespace-anchor->namespace
将其转换为命名空间,可以将其缩放为current-namespace
,也可以直接传入eval。
您的代码看起来像这样:
#lang racket
(define-namespace-anchor foo)
(define x 5)
(eval '(set! x 6) (namespace-anchor->namespace foo))
(displayln x)
会给您以下错误:
. set!: assignment disallowed;
cannot modify a constant
constant: x
这实际上是因为编译器将x
设置为静态变量,因为它认为它永远不会变异。 (因此可以在很多地方进行优化。)
您可以通过静态调用set!
一次来说服编译器它将被突变。
#lang racket
(define-namespace-anchor foo)
(define x (void))
(set! x 5)
(eval '(set! x 6) (namespace-anchor->namespace foo))
(displayln x)
这将打印出6
,这是您所期望的。
虽然请注意,这仅适用于set!
之类的内容。它并不意味着x
本身永远不会发生变异。例如,我们可以使用box
,unbox
和set-box!
#lang racket
(define-namespace-anchor foo)
(define x (box 5))
(eval '(set-box! x 6) (namespace-anchor->namespace foo))
(displayln (unbox x))
这也适用于任何可变的数据结构,例如向量,可变列表或可变哈希表。