【问题标题】:Racket: Using a macro across modulesRacket:跨模块使用宏
【发布时间】:2011-10-13 20:46:34
【问题描述】:

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

(increment! instance name field)

=>

(set-name-field 实例 (get-name-field 实例))

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

以下是演示该问题的人为示例。我想知道:

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

  2. 如何控制宏的扩展,使其在存在未在其原始上下文中找到的标识符的情况下运行?

谢谢。

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

【问题讨论】:

    标签: racket


    【解决方案1】:

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

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

    但是,这不是一个可靠的解决方案。当 setter 和 getter 有不同的名字时会发生什么?一个更强大的解决方案是使用结构名称并从中提取正确的信息(在语法时,在宏中)——有关详细信息,请参阅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)
    

    【讨论】:

    • 感谢@Eli Barzilay,注入上下文非常优雅。我同意该解决方案不是很健壮,我只是将其作为最终学习语法案例的好借口。我一定会尝试使用 struct-info 来改进它。
    猜你喜欢
    • 2014-12-31
    • 2012-10-24
    • 2015-11-18
    • 2017-10-14
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    相关资源
    最近更新 更多