使用 Racket 宏动态定义符号和宏

标签 dynamic macros lisp racket

我正在用 Racket 创建 DSL(领域特定语言)。在此 DSL 中,访问任何值都会导致它发生变异。换句话说,没有什么是纯粹的。我试图了解如何使用单个 Racket 宏来定义几个新符号和宏,以尝试减少重复代码。这个宏的一部分要求我引用一个私有(private)/隐藏值。例如。当我引用 a 时,它实际上是一个宏,它调用 getter get-val 获取私有(private)值 a_ (类似于符号宏普通的口齿不清)。我尝试按照文档生成带有宏的宏 here但它似乎并没有真正涵盖如何定义一个名为 a_ 的符号给定一个名为 a 的符号,因为它有点切线。深入了解 SO 也没有给我带来太多帮助,尽管我不确定我正在寻找的功能叫什么。

每个变量都使用相同的 getter 宏来改变过程中的值。这很好用。

(define-syntax-rule (get-val x)
  (begin (set! x (not x)) x))

在原始代码中,我需要像这样为每个符号手动定义一个 getter 宏。这也可以正常工作,但会导致大量重复代码。

(define a_ #f)
(define-syntax (a stx) #'(get-val a_))

(define b_ #f)
(define-syntax (b stx) #'(get-val b_))

我宁愿一次定义一个看起来像这样的主宏。 (不工作)。我相信 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_)))))

然后我可以像这样简单地创建很多值

(def 'a #f)
(def 'b #f)
; or ideally this (not quoted)
(def a #f)
(def b #f)

一旦它开始工作,我还将创建一个 peek 宏,它允许我检查值而无需为了调试目的而改变,尽管我可能会在知道如何创建第一个后自行解决这个问题宏。

最佳答案

Racket 称它们为 "identifier macros" .实际上,Racket 宏扩展器调用宏的转换器,无论它是“在运算符(operator)位置”还是“像变量”一样使用;不同之处在于转换器(通常使用 syntax-rulessyntax-casesyntax-parse 实现)接受的使用模式。

您是否需要像 a_ 这样的私有(private)变量可以访问?如果没有,您可以只使用一个常量名称,例如 tmphygiene将区分 (def a #f) 定义的 tmp(def b #f) 定义的 tmp >。然后你可以使用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)))))))

(此版本使用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_)))))))]))

关于使用 Racket 宏动态定义符号和宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69190589/

相关文章:

java - 堆栈的动态空间分配(JAVA)

clojure - 定义 Clojure 宏的同义词

c - 选择合适的错误报告机制 : How can I change a macro at compile time?

oop - EIEIO 组合(通过自己的槽暴露包含对象的槽)

lisp - 构建应用程序和调试器

c# - 使用父级的 DataContext(WPF - 动态菜单命令绑定(bind))

css - 容器固定宽度。中心 div 动态宽度。想要左右div平均填充剩余宽度

string - 在 Common Lisp 中将字符附加到字符串

javascript - 带有 html 内容的工具提示出现在右侧,有工作代码,但它仅适用于 1 个提示,我希望它是动态的

syntax - 非 Lisp 语言中有哪些宏系统?