如何在球拍中使用宏重命名过程



假设我想在编译时替换一个过程的所有出现,例如,将cons的所有出现替换为😄。我尝试了两个看起来很自然的选项:

1。

(define-syntax 😄
  (syntax-rules ()
    [(_ x y) (cons x y)]))

如果我做像(😄 2 3)这样的事情,但我不能使用😄,如果它不在应用程序位置,所以如果我做(map 😄 '(1 2) '(3 4)),我会得到一个"坏语法"错误。

然后我想只是替换标识符本身:

2。

(define-syntax 😄
  (λ (_) #'cons))

现在😄本身给了我#<procedure:cons>(map 😄 '(1 2) '(3 4))给出了正确的答案,但是对于(😄 2 3),语法转换器正在接受所有参数并将整个表达式替换为cons,这不是我想要的。


我如何实现我想要的?有没有什么变压器能做到这一点?

更新:当我输入最后一句话时,我调用了"橡皮鸭效应",找到了make-rename-transformer,这就是我想要的东西。然而,文档说"这样的转换器可以手动编写",似乎我没有这样做与我的2次尝试。如何手工制作这样的变压器?(忽略make-syntax-transformer文档中的项目符号)

同时,我知道我可以只使用(define 😄 cons),但那是一个完整的额外的函数调用在运行时

最简单(可能也是最好)的方法是使用rename-in以不同的名称导入标识符:

(require (rename-in racket/base [cons 😄]))

这也是最直接的方法,因为它根本不会创建语法转换器——它将创建一个在各个方面都与cons相同的新绑定,包括free-identifier=?。从编译器的角度来看,这使得cons😄无法区分。


然而,这有点神奇。另一种方法是使用make-rename-transformer:

手动创建一个重命名转换器
(define-syntax 😄 (make-rename-transformer #'cons))

如果不能使用rename-in,则使用重命名转换器是次佳选择,因为free-identifier=?函数专门识别重命名转换器,因此(free-identifier=? #'cons #'😄)仍将是#t。这对于关心语法绑定的宏很有用,因为😄将在所有cons可以接受的地方被接受。


仍然是在使用某种原语。如果你真的想这么做,你怎么能自己实现make-rename-transformer呢?这里的关键是宏应用程序可以在Racket中以两种形式出现。如果您有一个宏foo,那么它可以以以下两种方式使用:

foo
(foo ...)

第一种情况是" id宏",当宏用作裸标识符时,第二种是更常见的情况。但是,如果您想编写一个像表达式一样工作的宏,则需要考虑这两种情况。syntax-rules不能这样做,它不允许使用id宏,但是您可以使用syntax-id-rules:

做到这一点。
(define-syntax 😄
  (syntax-id-rules ()
    [(_ . args) (cons . args)]
    [_          cons]))

此模式将使😄在两个场景中按预期展开。然而,与上述两种方法不同的是,它将不与free-identifier=?合作,因为从宏扩展器的角度来看,😄现在是一个绑定到普通宏的全新绑定。


从这个角度来看,make-rename-transformer是神奇的吗?不完全是,但它是特殊的,因为free-identifier=?处理它作为一个特殊情况。如果您愿意,您可以实现自己的make-rename-transformer函数和自己的free-identifier=?函数来获得类似的行为:

(begin-for-syntax
  (struct my-rename-transformer (id-stx)
    #:property prop:procedure
    (λ (self stx)
      (with-syntax ([id (my-rename-transformer-id-stx self)])
        ((set!-transformer-procedure
          (syntax-id-rules ()
            [(_ . args) (id . args)]
            [_          id]))
         stx))))
  (define (my-rename-target id-stx)
    (let ([val (syntax-local-value id-stx (λ () #f))])
      (if (my-rename-transformer? val)
          (my-rename-target (my-rename-transformer-id-stx val))
          id-stx)))
  (define (my-free-identifier=? id-a id-b)
    (free-identifier=? (my-rename-target id-a)
                       (my-rename-target id-b))))

你可以这样做:

(define-syntax 😄 (my-rename-transformer #'cons))

…和(my-free-identifier=? #'😄 #'cons)将是#t。但是,不建议您实际上这样做,原因很明显:它不仅没有必要,而且不会真正起作用,因为其他宏不会使用my-free-identifier=?,只使用普通的free-identifier=?。因此,强烈建议您使用make-rename-transformer

如果您想手动编写这样的宏,您需要使用make-set!-transformer

http://docs.racket-lang.org/reference/stxtrans.html?q=set-transformer % 28 def。 % 28% 28 " ~ 23 ~ 25内核% 29. _make-set % 21-transformer % 29% 29日

请注意,您需要处理赋值(set! x e)和引用x,而应用程序(x arg0 arg1 ...)需要由syntax-case表达式中的子句处理。

更新

在Dybvig的书"The Scheme Programming Language"中,他有一个宏define-integrable的例子,听起来就是你要找的东西。

http://www.scheme.com/tspl4/syntax.html。/语法:s61

最新更新