为了在球拍中教自己更高级的宏,我开始创建一个宏来增加可变结构中的字段:
(increment! instance name field)
=>
(set-name-field instance (get-name-field instance))
我制作了一个有效的宏,并决定在多个模块之间共享会很有用 . 不幸的是,由于结构mutator不在定义宏的模块范围内,因此会发生扩展错误 .
以下是一个展示问题的人为例子 . 我想知道:
-
我是否以惯用球拍风格编写宏代码?这是正确的方法吗?
-
如何控制宏的扩展,以便它在存在未在其原始上下文中找到的标识符的情况下运行?
谢谢 .
#lang racket/load
(module util racket
(define-syntax increment!
(lambda (stx)
(syntax-case stx ()
[(increment! s sn fn i)
(with-syntax
([set! (string->symbol
(format "set-~a-~a!" (syntax-e #'sn) (syntax-e #'fn)))]
[get (string->symbol
(format "~a-~a" (syntax-e #'sn) (syntax-e #'fn)))])
#'(set! s (+ i (get s))))]
;; default increment of 1
[(increment! s sn fn) #'(increment! s sn fn 1)])))
(provide increment!)
)
(module bank racket
(require 'util)
(struct money (dollars pounds euros) #:mutable #:transparent)
(let ([m (money 0 50 20)])
(increment! m money pounds 100)
(increment! m money dollars)
m)
)
(require 'bank)
结果是
expand:模块中的未绑定标识符:set-money-pounds!
1 回答
您可以't do just that. The problem is that you'重新生成具有正确名称的符号,但您只需按原样返回符号,这意味着with-syntax为它们提供了一些默认(和错误)词汇上下文 . 相反,您应该使用datum->syntax并为其指定正确的上下文 .
下面是您的代码的修订版,它可以按预期工作 . 要了解更多相关信息,请参阅我最近关于不卫生宏主题的blog post .
但是,这不是一个强大的解决方案 . 当二传手和吸气者有不同的名字时会发生什么?更强大的解决方案是使用结构名称并从中提取正确的信息(在语法时,在宏中) - 有关详细信息,请参阅the manual . 在mailing list上询问有关它的问题也很好,因为可能有更好的方法来获得你想要的东西,或者如果你正在寻找一些类似点符号的功能,那就是更好的解决方案 .