Macros 定义可访问周围变量的嵌套racket宏

Macros 定义可访问周围变量的嵌套racket宏,macros,racket,Macros,Racket,我有一个宏,它扩展了racket语法,并在某个点上接受一系列bog标准racket表达式。这看起来像这样,相关的语法变量是body: (syntax-parse stx [(_ some-id:id body:expr ...+) 此宏使用生成的方法生成racket类,如下所示: #'(<class stuff> (define/public (some-id some-formal-parameter) body ...) 但这不允许我使用一些形

我有一个宏,它扩展了racket语法,并在某个点上接受一系列bog标准racket表达式。这看起来像这样,相关的语法变量是
body

(syntax-parse stx
  [(_ some-id:id
      body:expr ...+)
此宏使用生成的方法生成racket类,如下所示:

#'(<class stuff>
   (define/public (some-id some-formal-parameter)
     body ...)
但这不允许我使用
一些形式参数
,因为它没有定义。有没有合适的方法可以定义一些可以专门在主体中使用的东西,并且在扩展后仍然可以绑定到上下文中的变量?也许通过电话?可重用性是一大好处,因为这种“主体类型”可能存在于多个(类似)宏中

一些测试代码:

#lang racket
(require (for-syntax syntax/parse))

(define-syntax (define-something stx)
  (syntax-parse stx
    [(_ some-id:id
        body:expr ...+)
     #'(define some-id
         (new
          (class object%
            (super-new)
            (define/public (displ arg)
              (displayln arg))
            (define/public (tick some-formal-parameter) 
              body ...))))]))

(define-syntax-rule (tweet value)
  (send this displ value))


(define-something derp
  (define a 'not-derp)
  (tweet a))

(send derp tick 'derp)

现在我知道(并且可以回答)我想问的问题:如何定义只能在特定上下文中使用的宏(对我来说:在racket类的方法体中),以及如何使用动态绑定变量。在上面的原始代码中:当使用表达式
(tweet a)
时,我不仅需要
a
的值,还需要
一些形式参数的值,这些参数在tweet宏展开的代码上下文中绑定(未定义)

ChrisJester Young善意地向我指出语法参数,它似乎确实解决了动态绑定问题,并且“只能在某些上下文中使用”。Eli Barzilay、Ryan Culpeper和Matthew Flatt的一篇文章帮助我理解了语法参数

关于我发布的原始示例代码,这是我提出的解决方案:

#lang racket

(require
  racket/stxparam
  (for-syntax syntax/parse))

(define-syntax-parameter tweet
  (lambda (stx)
    (raise-syntax-error 'tweet "use of an actor keyword outside of the body of an actor" stx)))

(define-syntax (define-something stx)
  (syntax-parse stx
    [(_ some-id:id
        body:expr ...+)
     #'(define some-id
         (new
          (class object%
            (super-new)

            (define/public (tick some-formal-parameter)
              (syntax-parameterize
               ([tweet
                 (syntax-rules ()
                   [(_ value)
                    (begin (displayln some-formal-parameter)
                           (displayln value))])])
               body ...)
              ))))]))

(define-something derp
  (define a 'not-derp)
  (tweet a))

(send derp tick 'derp)
以下是三个重点注意事项

  • 由于
    tweet
    宏的定义,每当它在
    语法参数化
    语句的上下文之外使用时(该语句更改
    tweet
    的定义),它都会抛出相应的错误
  • 在类的公共方法
    的主体中,我们将
    tweet
    的定义更改为一个宏,该宏与
    (\uvalue)
    形式的模式相匹配(这是我们提供给
    tweet
    的值)
  • tweet宏可以扩展为既使用绑定值
    ,又使用
    某个形式参数
    的值,不管是什么

  • 我不知道这是否是处理这种情况的正确方法,但它似乎很好。

    我不完全理解您的问题,但据我所知,语法参数似乎是实现它的方法。如果你能把你之前和之后的期望都公布出来,我可以试着编一些东西。:-)是的,很抱歉。“用宏思考”很难理解。有那么多要学,那么多要知道,那么多你必须知道。我正在读一篇关于语法参数的文章,看起来这可以解决这个问题。我不知道它是否能很好地解决这个问题,但我会尝试一些东西并报告:)很好的解决方案!但是,您可能注意到,您不能在
    define something
    的主体中引用
    一些形式参数,因为它们在不同的词汇范围内。也许这没关系,但如果您确实希望能够做到这一点,那么解决方案将是
    某些形式参数的另一个语法参数。这是因为现在在
    定义某物
    的主体中有两件事情是可用的。对于我遇到的特定问题,
    定义某物
    的主体不能使用
    一些形式参数
    ,这确实不是一个问题。事实上,这是一个身体不应该意识到的参数,因此它不在词汇范围内的事实实际上是很好的。谢谢你的评论。这是对我的解决方案的一个很好的验证,当其他人偶然发现这个问题时,他们可能会发现这个见解很有用:)
    #lang racket
    
    (require
      racket/stxparam
      (for-syntax syntax/parse))
    
    (define-syntax-parameter tweet
      (lambda (stx)
        (raise-syntax-error 'tweet "use of an actor keyword outside of the body of an actor" stx)))
    
    (define-syntax (define-something stx)
      (syntax-parse stx
        [(_ some-id:id
            body:expr ...+)
         #'(define some-id
             (new
              (class object%
                (super-new)
    
                (define/public (tick some-formal-parameter)
                  (syntax-parameterize
                   ([tweet
                     (syntax-rules ()
                       [(_ value)
                        (begin (displayln some-formal-parameter)
                               (displayln value))])])
                   body ...)
                  ))))]))
    
    (define-something derp
      (define a 'not-derp)
      (tweet a))
    
    (send derp tick 'derp)