当前位置:  开发笔记 > 编程语言 > 正文

在球拍宏扩展期间可以使用运行时信息吗?

如何解决《在球拍宏扩展期间可以使用运行时信息吗?》经验,为你挑选了1个好方法。

假设我在运行时有一个哈希表,其中包含字符串作为键.宏可以访问此信息并let从中构建表达式吗?

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

(magic-let env (+ a b)) ; 444

我知道我可以identifier-binding通过在散列表中使用查找替换未定义的标识符来解决这个问题,但是阴影将无法正常工作let.

标记scheme也是因为我认为它的宏系统是相似的.



1> Alexis King..:

不,你做不到.至少不是你描述的方式.

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

由于多种原因,这种保证非常重要,但这对于这个问题的讨论过于笼统.这是相关讨论特定的问题,这就是为什么绑定本身必须是静态的.

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

这实际上是一件非常好的事情 - 它足够强大,足以支持非常复杂的系统,这些系统在没有卫生的情况下更难管理 - 但它引入了一些限制:

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

    此外,范围规则更复杂,因为宏引入的绑定存在于它们自己的范围内.因此,绑定范围不一定是严格的词法.

magic-let象你所说的不能完全工作,因为编译器不可能推导出绑定ab静态.但是,所有这些都不会丢失:您可以#%top在遇到未绑定的标识符时挂入扩展器引入的神奇标识符.您可以使用它来使用哈希查找替换未绑定的值,并且可以使用语法参数#%top在每个值内进行卫生调整magic-let.这是一个例子:

#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

我不建议这样做,因为它违背了很多Racket的理念,它可能会导致一些难以发现的错误.可能有更好的方法来解决你的问题,而不会滥用像#%top.如果你真的想要,它仍然可能的.

推荐阅读
凹凸曼00威威_694
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有