首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在定义语法体中数据->语法和语法#‘之间有什么区别?

在定义语法体中数据->语法和语法#‘之间有什么区别?
EN

Stack Overflow用户
提问于 2018-04-05 09:43:27
回答 1查看 617关注 0票数 8

测试代码:

代码语言:javascript
复制
(define-syntax (test-d stx)
  #'(begin 
      (define (callme a)
        (writeln a))))

(define-syntax (test-e stx)
  (datum->syntax stx '(begin 
                         (define (callme2 a)
                           (writeln a)))))


> (test-d)
> (callme 1)
. . callme: undefined;
 cannot reference an identifier before its definition
> (test-e)
> (callme2 1)
1

我不明白测试-d和测试-e的区别。在我看来他们是平等的。不过,callme还没有定义。

就连宏步行者也说这是一样的。

代码语言:javascript
复制
Expansion finished
(module anonymous-module racket
  (#%module-begin
   (define-syntax (test-d stx)
     #'(begin (define (callme a) (writeln a))))
   (define-syntax (test-e stx)
     (datum->syntax
      stx
      '(begin (define (callme2 a) (writeln a)))))
   (begin (define (callme a) (writeln a)))
   (begin (define (callme2 a) (writeln a)))))

我猜在test-d中缺少一些信息(上下文?)在test-e中通过stx传递。

我如何才能用只使用#‘来定义callme呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-04-05 16:43:03

球拍的宏观系统是卫生的。这意味着宏引入的标识符存在于它们自己的范围内--它们不会与在宏之外使用或定义的标识符发生冲突。这通常是您想要的,因为它避免了当宏作者和宏用户决定使用相同的变量名时出现的问题。

但是,在您的情况下,您需要的行为显然是不卫生的。您希望宏定义一个新的标识符,并使该标识符位于宏之外的作用域中。幸运的是,虽然球拍在默认情况下强制卫生,但它允许你打破(或“弯曲”)卫生,当你想。

当您使用#',又名syntax时,您使用的是卫生的宏特性。这意味着您对callme的定义仅在test-d中可见,并且在调用代码中是不可见的。然而,datum->syntax是允许您打破卫生规则的主要机制之一:它“伪造”了一种新的语法,它与另一种语法(在您的例子中是stx )的作用域相同,后者是宏的输入。这就是为什么callme2test-e定义之外是可见的。

然而,这是一个沉重的锤子…其实太重了。您的test-e宏非常不卫生,这意味着如果宏的用户绑定了test-e使用的名称,它可能会中断。例如,如果用户定义一个名为begin的局部变量,则test-e将不再工作:

代码语言:javascript
复制
(define-syntax (test-e stx)
  (datum->syntax stx '(begin 
                        (define (callme2 a)
                          (writeln a)))))

(let ([begin 42])
  (test-e)
  (callme2 1))
代码语言:javascript
复制
define: not allowed in an expression context

你可以避免这个问题,对你如何打破卫生更加保守。实际上,在这种情况下,我们希望不卫生的宏中唯一不卫生的部分是callme2标识符,因此我们可以使用datum->syntax来伪造这段语法,但是对所有其他宏使用#'

代码语言:javascript
复制
(define-syntax (test-e stx)
  (with-syntax ([callme-id (datum->syntax stx 'callme2)])
    #'(begin
        (define (callme-id a)
          (writeln a)))))

(let ([begin 42])
  (test-e)
  (callme2 1))

现在这个程序起作用了,它只是在一个需要的地方不卫生。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49669142

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档