sco*_*855 2 lisp macros dynamic racket
我正在 Racket 中创建 DSL(域特定语言)。在此 DSL 中,访问任何值都会导致其发生变化。换句话说,没有什么是纯粹的。我试图了解如何使用单个球拍宏定义几个新的符号和宏,以尝试减少重复的代码。该宏的一部分要求我引用私有/隐藏值。当我引用 EG 时,它实际上是一个宏,它调用私有值的a
getter (类似于 common lisp 中的符号宏)。我试图遵循此处使用宏生成宏的文档,但它似乎并没有真正涵盖如何定义一个名为给定名称的符号,因为它有点切线。尽管我不确定我正在寻找的功能叫什么,但挖掘 SO 也没有给我带来太多好处。get-val
a_
a_
a
每个变量都使用相同的 getter 宏,该宏会在进程中改变值。这很好用。
(define-syntax-rule (get-val x)
(begin (set! x (not x)) x))
Run Code Online (Sandbox Code Playgroud)
在原始代码中,我需要为每个符号手动定义一个 getter 宏,如下所示。这也可以正常工作,但会导致大量重复代码。
(define a_ #f)
(define-syntax (a stx) #'(get-val a_))
(define b_ #f)
(define-syntax (b stx) #'(get-val b_))
Run Code Online (Sandbox Code Playgroud)
我更愿意定义一个主宏一次,它看起来像这样。(不工作)。我相信这string->symbol
可能就是问题所在。
(define-syntax-rule (def name val)
(begin
; Create new varname with name followed by underscore
; This should probably be a `let` ?
(define name_ (string-append name "_"))
; Assign the value to that (private) symbol.
(define (string->symbol name_) val)
; Create public getter macro
(define-syntax (name stx) #'(get-val (string->symbol name_)))))
Run Code Online (Sandbox Code Playgroud)
然后我可以简单地创建很多像这样的值
(def 'a #f)
(def 'b #f)
; or ideally this (not quoted)
(def a #f)
(def b #f)
Run Code Online (Sandbox Code Playgroud)
一旦它起作用,我还将创建一个peek
宏,它允许我检查该值,而无需出于调试目的而进行变异,尽管一旦我知道如何创建第一个宏,我可能可以自己弄清楚这一点。
Racket 称它们为“标识符宏”。实际上,Racket 宏扩展器调用宏的转换器,无论它是“在操作符位置”使用还是“像变量一样”使用;区别在于变压器(通常用syntax-rules
、syntax-case
或实现syntax-parse
)接受的使用模式。
您是否需要像这样的私有变量a_
可以访问?如果没有,您可以只使用单个常量名称,例如tmp
,并且卫生将区分tmp
定义者(def a #f)
和tmp
定义者(def b #f)
。然后,您可以将make-variable-like-transformer
定义名称的类似变量的使用转换为获取和更新值的表达式,如下所示:
(require (for-syntax racket/base syntax/transformer))
(define-syntax-rule (def name val)
(begin
(define tmp val)
(define-syntax name
(make-variable-like-transformer
#'(begin0 tmp (set! tmp (not tmp)))))))
Run Code Online (Sandbox Code Playgroud)
(此版本用于begin0
在更新之前获取值,但当然您可以将其更改回来。)
如果您确实希望a_
变量可访问,那么您可以使用format-id
创建名称,但不能再使用define-syntax-rule
来执行此操作。
(require (for-syntax racket/base racket/syntax syntax/transformer))
(define-syntax (def stx)
(syntax-case stx ()
[(def name val)
(with-syntax ([name_ (format-id #'name "~a_" #'name)])
#'(begin
(define name_ val)
(define-syntax name
(make-variable-like-transformer
#'(begin0 name_ (set! name_ (not name_)))))))]))
Run Code Online (Sandbox Code Playgroud)