Racket:跨模块使用宏

时间:2011-07-27 00:09:56

标签: racket

为了在球拍中教自己更高级的宏,我开始创建一个宏来增加可变结构中的字段:

(increment! instance name field)

=>

(set-name-field instance (get-name-field instance))

我制作了一个可行的宏,并决定在多个模块之间共享会很有用。不幸的是,由于结构mutator不在定义宏的模块范围内,因此会发生扩展错误。

以下是一个展示问题的人为例子。我想知道:

  1. 我是否以惯用球拍风格编写宏代码?这是正确的做法吗?

  2. 如何控制宏的扩展,以便在原始上下文中找不到标识符的情况下进行操作?

  3. 感谢。

    #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)
    

    结果

      

    展开:模块中的未绑定标识符:set-money-pounds!

1 个答案:

答案 0 :(得分:3)

你做不到这一点。问题是您正在生成具有正确名称的符号,但您只是按原样返回符号,这意味着with-syntax为它们提供了一些默认(和错误)词汇上下文。相反,您应该使用datum->syntax并为其指定正确的上下文。

以下是您所期望的代码修订版。要了解有关它的更多信息,请参阅我最近关于不卫生宏主题的blog post

但是,这不是一个强大的解决方案。当二传手和吸气者有不同的名字时会发生什么?更强大的解决方案是使用结构名称并从中提取正确的信息(在语法时,在宏中) - 有关详细信息,请参阅the manual。在mailing list上询问有关它的问题也很好,因为可能有更好的方法来获得你想要的东西,或者如果你正在寻找一些类似点符号的功能,那就是更好的解决方案。

#lang racket/load

(module util racket
  (define-syntax increment!
    (lambda (stx)
      (syntax-case stx ()
        [(increment! s sn fn i)
         (let ([id (lambda (fmt)
                     (let ([str (format fmt (syntax-e #'sn) (syntax-e #'fn))])
                       (datum->syntax #'sn (string->symbol str))))])
           (with-syntax ([set! (id "set-~a-~a!")]
                         [get (id "~a-~a")])
             #'(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)