假设我想在编译时替换所有出现的过程,例如cons
所有出现的。我尝试了两种似乎很自然的选择:
(define-syntax
(syntax-rules ()
[(_ x y) (cons x y)]))
如果我执行类似( 2 3)
的操作,则此功能正常,但如果它不在应用程序位置,则无法使用,因此如果
(map '(1 2) '(3 4))
,我会收到“错误的语法”错误}。
然后我想只更换标识符本身:
(define-syntax
(λ (_) #'cons))
现在本身就会给我
#<procedure:cons>
和(map '(1 2) '(3 4))
给出正确的答案,但是( 2 3)
语法变换器会获取所有参数并将整个表达式替换为cons
,这不是我想要的。
我如何实现我的目标?是否有某种变压器可以做到这一点?
更新:当我输入最后一句时,我调用了“橡皮鸭效应”并找到了make-rename-transformer
这就是我想要的东西。然而,文档说“这样的变换器可以手动编写”,似乎我没有用我的2次尝试这样做。 如何手动制作这样的变压器?(忽略make-syntax-transformer
文档中的要点
另外,我知道我可以使用(define cons)
,但那是在运行时调用的额外函数。
答案 0 :(得分:3)
执行此操作的绝对最简单(也可能是最佳)方法是使用rename-in
以不同的名称导入标识符:
(require (rename-in racket/base [cons ]))
这也是最直接的方法,因为它根本不会创建语法转换器 - 它会以各种方式创建一个与cons
相同的新绑定,包括{{3} }。从编译器的角度来看,这使得cons
和无法区分。
然而,这有点神奇。另一种方法是使用free-identifier=?
:
(define-syntax (make-rename-transformer #'cons))
如果你不能使用rename-in
,那么使用重命名变换器是下一个最好的选择,因为make-rename-transformer
函数特别识别重命名变换器,因此(free-identifier=? #'cons #')
仍然是{{1} }}。这对于关心语法绑定的宏非常有用,因为#t
将在所有相同的位置被接受。
不过,这是使用某种原始的。如果你真的想,你怎么能自己实现cons
?那么,关键是宏应用程序可以在Racket中以两种形式出现。如果你有一个宏make-rename-transformer
,那么它可以用于以下任何一种方式:
foo
第一种情况是“id宏”,当宏用作裸标识符时,第二种情况是更常见的情况。但是,如果你想编写一个像表达式一样工作的宏,你需要担心这两种情况。您不能使用不允许使用ID宏的foo
(foo ...)
来执行此操作,但可以使用syntax-rules
执行此操作:
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=?
。
答案 1 :(得分:2)
如果您手动想要编写此类宏,则需要使用make-set!-transformer
。
请注意,您需要处理分配(set! x e)
和引用x
,而应用(x arg0 arg1 ...)
需要由syntax-case
表达式中的子句处理。
更新
在Dybvig的书&#34; The Scheme Programming Language&#34;他有一个宏define-integrable
的例子,听起来就像你追求的那样。