Macros 在racket中的宏扩展期间可以使用运行时信息吗?

Macros 在racket中的宏扩展期间可以使用运行时信息吗?,macros,scheme,racket,Macros,Scheme,Racket,假设我在运行时有一个以字符串作为键的哈希表。宏是否可以访问此信息并从中构建let表达式 (define env (hash 'a 123 'b 321)) (magic-let env (+ a b)) ; 444 我知道我可以通过在哈希表中用查找替换未定义的标识符来破解标识符绑定,但是阴影将不会像在正常的let中那样工作 标记scheme,因为我假设它的宏系统是类似的。不,你不能这样做。至少不是你描述的那样 无法在宏中访问运行时值的一般原因很简单:宏在编译时完全展开。编译程序时,运行时值根

假设我在运行时有一个以字符串作为键的哈希表。宏是否可以访问此信息并从中构建
let
表达式

(define env (hash 'a 123 'b 321))

(magic-let env (+ a b)) ; 444
我知道我可以通过在哈希表中用查找替换未定义的标识符来破解
标识符绑定
,但是阴影将不会像在正常的
let
中那样工作


标记
scheme
,因为我假设它的宏系统是类似的。

不,你不能这样做。至少不是你描述的那样

无法在宏中访问运行时值的一般原因很简单:宏在编译时完全展开。编译程序时,运行时值根本不存在。一个程序可以被编译,字节码可以放在另一台计算机上,这台计算机将在几周后运行。宏观扩张已经发生。无论运行时发生什么,程序都不会改变

由于种种原因,这种保证变得极其重要,但对于这个问题来说,这种讨论太笼统了。讨论一个特定的问题是相关的,这就是为什么绑定本身需要是静态的

在Racket中,只要您在一个模块内(即不在顶层/REPL),所有绑定都可以在编译时静态解析。这在其他编程语言中是一个非常有用的属性,主要是因为编译器可以生成效率更高的优化代码,但在Racket或Scheme中尤其重要。这是因为宏系统是如何运行的:在一种带有卫生宏的语言中,作用域是复杂的

这实际上是一件非常好的事情,它足够强大,可以支持非常复杂的系统,如果没有卫生设施,这些系统将更难管理,但它引入了一些约束:

  • 由于每个绑定都可以是宏或运行时值,因此需要提前知道绑定以执行程序扩展。编译器需要知道它是需要执行宏扩展还是仅仅发出一个变量引用

  • 此外,作用域规则要复杂得多,因为宏引入的绑定位于它们自己的作用域中。因此,绑定作用域不需要严格按照词法定义

  • 您的
    magic let
    无法像您描述的那样工作,因为编译器不可能静态地推断
    a
    b
    的绑定。然而,一切都没有丢失:您可以连接到
    #%top
    ,这是一个神奇的标识符,当遇到未绑定的标识符时由扩展器引入。您可以使用它来用散列查找替换未绑定的值,还可以使用它在每个
    magic let
    中卫生地调整
    #%top
    。下面是一个例子:

    #lang racket
    
    (require (rename-in racket/base [#%top base-#%top])
             racket/stxparam)
    
    (define-syntax-parameter #%top (make-rename-transformer #'base-#%top))
    
    (define-syntax-rule (magic-let env-expr expr ...)
      (let ([env env-expr])
        (syntax-parameterize ([#%top (syntax-rules ()
                                       [(_ . id) (hash-ref env 'id)])])
          (let () expr ...))))
    
    (magic-let (hash 'a 123 'b 321) (+ a b)) ; => 444
    
    当然,请记住,这将用哈希查找替换所有未绑定的标识符。这有两方面的影响。首先,它不会对已绑定的标识符进行阴影处理:

    (let ([a 1])
      (magic-let (hash 'a 2)
        a)) ; => 1
    
    这可能是最好的,只是为了让事情保持半清醒。这还意味着以下操作将引发运行时异常,而不是编译时错误:

    (magic-let (hash 'a 123) (+ a b))
    ; hash-ref: no value found for key
    ;   key: 'b
    

    我不建议这样做,因为这违背了很多球拍哲学,而且可能会导致一些难以发现的bug。可能有更好的方法来解决您的问题,而不滥用
    #%top
    。不过,如果你真的想要的话,这是可能的。

    我有点困惑。你能告诉我你想让magic let做什么吗?简而言之,答案是否定的,但你可以让生成信息的代码同时出现在宏代码和运行时代码中,并且可以使用
    语法局部值
    进行检索。当我对你的问题有更多的了解时,我很乐意写出一个更全面的答案。否则我只能指出这篇论文: