考虑一种非常简单的参与者语言,其中一个参与者定义了一些本地状态和一些方法,这些方法可以通过向参与者发送消息来调用。在其实现中,可以将参与者的一种方法转换为一个函数,该函数定义该方法的形式参数并接受该参与者的当前本地状态。调用该方法将返回新的本地状态。
约束身体中的形式参数是没有问题的,但是约束局部状态似乎更困难。在下面代码末尾的示例中,在save方法的主体中,a将保持未绑定,尽管a (一个不同的a)被METHOD宏中生成的evaluate-body函数绑定。因此,下面的代码示例中的关键点是METHOD宏,更具体地说是evaluate-body函数(这是应该进行绑定的地方,这意味着我的程序设计是合理的)。
是否有一种方法可以卫生地绑定这组任意的空闲标识符(目前只包含a,但它可能是任何东西,真的)?
#lang racket
(require (for-syntax syntax/parse))
(require racket/stxparam)
(struct actor (local-state methods))
(struct method (name formal-parameters body))
(define-syntax-parameter local-state-variables #f)
(define-syntax (ACTOR stx)
(syntax-parse stx
[(_ (LOCAL_STATE state-variable ...) method:expr ...+)
#'(syntax-parameterize ([local-state-variables '(state-variable ...)])
; For the sake of simplicity, an actor is currently a list of message handlers
(actor
(make-list (length '(state-variable ...)) (void))
(list method ...)))]))
(define-syntax (METHOD stx)
(syntax-parse stx
[(_ (name:id formal-parameter:id ...) body:expr ...+)
(with-syntax ([(local-state-variable ...) (syntax-parameter-value #'local-state-variables)])
#'(method
'name
'(formal-parameter ...)
(λ (formal-parameter ... #:local-state [current-state '()])
; the "a" that will be bound here is different from the free identifier "a" in the body
(define (evaluate-body local-state-variable ...)
body ...
(list local-state-variable ...))
(apply evaluate-body current-state))))]))
(ACTOR (LOCAL_STATE a)
(METHOD (save new-a)
; "a" is an unbound identifier
(set! a new-a)))发布于 2016-12-14 21:06:33
为了使本地状态变量具有适当的词法上下文,需要将它们存储为标识符,而不是符号。也就是说,在ACTOR宏的结果中,需要将syntax-parameterize更改为:
#'(syntax-parameterize ([local-state-variables #'(state-variable ...)])
#| rest of the template (unchanged)... |#)注意将quote/'替换为syntax/#'。这将存储标识符及其词法上下文,而不是作为符号。
下一步是在METHOD宏中正确地引入它们。为此,只需将syntax-local-introduce应用于语法参数的值,该参数将向标识符添加宏导入范围。您还可以将with-syntax替换为syntax-parse的#:with子句,以稍微简化一些事情,因此整个宏如下:
(define-syntax (METHOD stx)
(syntax-parse stx
[(_ (name:id formal-parameter:id ...) body:expr ...+)
#:with (local-state-variable ...)
(syntax-local-introduce (syntax-parameter-value #'local-state-variables))
#'(method #| rest of the template (unchanged)... |#)]))这会管用的。
这里需要syntax-local-introduce的原因可能有点令人困惑,但最直观的思考方法是,考虑到Racket目前使用的“范围集”卫生模型。为了使宏引入的绑定不与用户定义的绑定冲突,语法转换器返回的每个语法都有一个新的作用域,这个范围永远不会附加到用户编写的任何内容。当然,结果中的一些语法是用户提供的语法,因此宏扩展程序需要确保它不将新的作用域附加到这些语法对象。
一般来说,不可能确定哪些语法对象应该由用户提供,因为宏作者可以“弯曲”卫生,并从其他对象创建新的语法对象。幸运的是,解决方案简单而优雅:只需将宏导入作用域附加到用户提供的所有语法对象上,然后将它们传递给宏,然后在结果中的所有语法块上翻转作用域。这样,在翻转发生后,用户提供的语法对象将不具有宏导入范围。
syntax-local-introduce函数允许您手动翻转这个特殊作用域。在这种情况下,由于local-state-variables的值应该被视为宏的输入,但是宏扩展程序不会自动给出宏导入作用域(因为它不是宏的直接输入),所以您必须自己添加作用域。这样,宏扩展程序将在宏展开后删除作用域,标识符将以正确的词法上下文结束。
https://stackoverflow.com/questions/41139868
复制相似问题