Chr*_*oks 3 macros scheme racket
假设我想在编译时替换所有出现的过程,例如所有出现的conswith 。我尝试了两种看起来很自然的选择:
(define-syntax \n (syntax-rules ()\n [(_ x y) (cons x y)]))\nRun Code Online (Sandbox Code Playgroud)\n\n如果我做类似的事情,这是可行的,但如果它不在应用程序位置,( 2 3)我就无法使用,所以如果我这样做,我会收到“错误语法”错误。(map \'(1 2) \'(3 4))
然后我想只替换标识符本身:
\n\n(define-syntax \n (\xce\xbb (_) #\'cons))\nRun Code Online (Sandbox Code Playgroud)\n\n现在它本身给了我#<procedure:cons>并(map \'(1 2) \'(3 4))给出了正确的答案,但是使用( 2 3)语法转换器正在获取所有参数并将整个表达式替换为cons,这不是我想要的。
我如何实现我想要的?有某种变压器可以做到这一点吗?
更新:当我输入最后一句话时,我调用了“橡皮鸭效应”并找到了make-rename-transformer我想要的东西。然而,文档说“这样的变压器可以手动编写”,而且我的两次尝试似乎都未能做到这一点。如何手动制作这样的变压器?(忽略文档中的要点make-syntax-transformer)
另外,我知道我可以使用(define cons),但那是运行时的整个额外函数调用。
绝对最简单(也可能是最好)的方法是使用rename-in不同名称导入标识符:
(require (rename-in racket/base [cons ]))\nRun Code Online (Sandbox Code Playgroud)\n\n这也是最直接的方法,因为它根本不会创建语法转换器 xe2x80x94 它会创建一个在各个cons方面都相同的新绑定,包括free-identifier=?. 从编译器\xe2\x80\x99s 的角度来看,这cons使得无法区分。
然而,这有点神奇。另一种方法是使用手动创建重命名转换器make-rename-transformer:
(define-syntax (make-rename-transformer #\'cons))\nRun Code Online (Sandbox Code Playgroud)\n\n如果您可以\xe2\x80\x99t 使用rename-in,那么使用重命名转换器是下一个最好的事情,因为该free-identifier=?函数专门识别重命名转换器,所以(free-identifier=? #\'cons #\')仍然是#t。这对于关心语法绑定的宏很有用,因为将在所有相同的地方被接受cons。
尽管如此,这还是使用了某种原语。如果你真想这么做,你make-rename-transformer自己该如何实现呢?嗯,这里的关键是宏应用在Racket中可以以两种形式出现。如果你有宏foo,则可以通过以下任一方式使用它:
foo\n(foo ...)\nRun Code Online (Sandbox Code Playgroud)\n\n第一种情况是 \xe2\x80\x9cid 宏\xe2\x80\x9d,当宏用作裸标识符时,第二种情况是更常见的情况。但是,如果您想编写一个像表达式一样工作的宏,则需要担心这两种情况。您不能使用 执行此操作syntax-rules,它不允许\xe2\x80\x99t 允许 id 宏,但您可以使用syntax-id-rules:
(define-syntax \n (syntax-id-rules ()\n [(_ . args) (cons . args)]\n [_ cons]))\nRun Code Online (Sandbox Code Playgroud)\n\n在这两种情况下,这种模式都会按预期进行扩展。然而,与上述两种方法不同的是,它不会与合作free-identifier=?,因为现在从 Macroexpander\xe2\x80\x99s 的角度来看,它是一个绑定到普通宏的全新绑定。
从这个角度来看,make-rename-transformer神奇吗?free-identifier=?并非如此,但从将其作为特殊情况处理的意义上来说,它是特殊的。如果你愿意,你可以实现你自己的make-rename-transformer函数和你自己的free-identifier=?函数来获得类似的行为:
(begin-for-syntax\n (struct my-rename-transformer (id-stx)\n #:property prop:procedure\n (\xce\xbb (self stx)\n (with-syntax ([id (my-rename-transformer-id-stx self)])\n ((set!-transformer-procedure\n (syntax-id-rules ()\n [(_ . args) (id . args)]\n [_ id]))\n stx))))\n\n (define (my-rename-target id-stx)\n (let ([val (syntax-local-value id-stx (\xce\xbb () #f))])\n (if (my-rename-transformer? val)\n (my-rename-target (my-rename-transformer-id-stx val))\n id-stx)))\n\n (define (my-free-identifier=? id-a id-b)\n (free-identifier=? (my-rename-target id-a)\n (my-rename-target id-b))))\nRun Code Online (Sandbox Code Playgroud)\n\n这将允许您执行以下操作:
\n\n(define-syntax (my-rename-transformer #\'cons))\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6 并且(my-free-identifier=? #\' #\'cons)将是#t. 但是,\xe2\x80\x99s 不建议您实际执行此操作,原因很明显:不仅没有必要,而且它不会 \xe2\x80\x99t 真正起作用,因为其他宏不会\xe2\x80\x99t 使用my-free-identifier=?,只是普通而已free-identifier=?。因此,强烈建议您使用它\xe2\x80\x99 make-rename-transformer。