macros - 用于将 getter 函数包装在标识符周围的 Racket 宏

标签 macros racket wrapper setter getter

我想在标识符周围封装一个 getter 和 setter 函数,这样

(define x 1)
(set! x v) ; should expand to (custom-set! x v)
x          ; should expand to (custom-get x)

其中 custom-put!custom-get 在其他地方定义并添加其他行为,例如日志记录。

racket guide (第 16.1.6 节),有一个 Set!-Transformer 的示例,它适用于具有零参数的 setter 和 getter。但是,我需要一个宏,它将标识符的出现扩展为 getter 函数,并以匹配的标识符作为其参数。

我尝试采用链接部分中的宏,如下所示:

(define-syntax-rule (generate-accessors id get put!)
(define-syntax id
  (make-set!-transformer
   (lambda (stx)
       (syntax-case stx (set! get)
         [id (identifier? (syntax id)) (syntax (get id))]
         [(set! id e) (syntax (put! id e))])))))

问题是(syntax (get id))导致无限扩展。 有没有一种直接的方法来切断模板表达式的递归?我还考虑过生成一个绑定(bind)到 id 的新标识符并将其包含在文字列表中,但我不知道如何实现这一点。

最佳答案

您链接到的示例适用于上一节中的代码 定义了 get-valput-val!:

(define-values (get-val put-val!)
  (let ([private-val 0])
    (values (lambda () private-val)
            (lambda (v) (set! private-val v)))))

(define-syntax val
  (make-set!-transformer
   (lambda (stx)
     (syntax-case stx (set!)
       [val (identifier? (syntax val)) (syntax (get-val))]
       [(set! val e) (syntax (put-val! e))]))))

val ;; 0
(set! val 42)
val ;; 42

让我们编写宏编写宏,generate-accessors, 参数化标识符和访问器,并查看它是否有效:

(define-syntax (generate-accessors stx)
  (syntax-case stx ()
    [(_ val get-val put-val!)
     (syntax
      (define-syntax val
        (make-set!-transformer
         (lambda (stx)
           (syntax-case stx (set!)
             [val (identifier? (syntax val)) (syntax (get-val))]
             [(set! val e) (syntax (put-val! e))])))))]))

(generate-accessors foo get-val put-val!)
foo ;; 0
(set! foo 42)
foo ;; 42

另外,让我们用另一对访问器来练习它,它显示了 像“日志记录”这样的副作用:

(define-values (custom-get custom-set!)
  (let ([private-val 0])
    (values (lambda () (println "get") private-val)
            (lambda (v) (println "set") (set! private-val v)))))

(generate-accessors bar custom-get custom-set!)
bar ;; 0
(set! bar 42)
bar ;; 42

编辑:为了回应您的评论,这里有一个变体,它为每次调用生成一对新的访问器(和值)。它允许您提供一对要调用的运行时函数来完成额外的工作(这里只是 println)。 (如果您发现它总是相同的东西,您可以将其简化为硬编码 printlnlog-debug 或其他内容。)

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

(define-syntax (define/logged stx)
  (syntax-parse stx
    [(_ id:id init:expr on-get:expr on-set:expr)
     #:with get (format-id stx "get-~a" #'id)
     #:with set (format-id stx "set!-~a" #'id)
     #'(begin
         (define-values (get set)
           (let ([v init])
             (values (λ () (on-get 'id v) v)
                     (λ (e) (on-set 'id e) (set! v e)))))
         (define-syntax id
           (make-set!-transformer
            (λ (stx)
              (syntax-parse stx
                [id:id #'(get)]
                [(set! id e) #'(set e)])))))]))

(define (on-get id v)
  (println `(get ,id ,v)))

(define (on-set! id v)
  (println `(set! ,id ,v)))

(define/logged foo 0 on-get on-set!)
foo
(set! foo 42)
foo

;; prints:
;; '(get foo 0)
;; 0
;; '(set! foo 42)
;; '(get foo 42)
;; 42

我不是 100% 清楚你想要做的事情的背景。也许这更接近,或者至少给你一些想法。

请注意,我将切换到syntax-parse,而不是尝试遵循指南中的原始示例。只是因为。

关于macros - 用于将 getter 函数包装在标识符周围的 Racket 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44790394/

相关文章:

c# - 使用 C# 为 Outlook 添加隐藏密件抄送

recursion - 新方案/ Racket : Heavy use of recursion a way of life or am I just going through a typical phase

racket - 为什么这个调用 (read in-from-make-pipe) 在 Racket 中阻塞?

java - 如何使对象像 boolean 值一样使用 booleanValue() ?

scala 宏生成隐式

swift - 你能从调用者那里得到 __FUNCTION__ 吗?

macros - 使用宏构建具有动态输入的函数

functional-programming - 将 curry 映射到参数列表

c# - 通用函数包装器

c# - C# 托管代码和 C++ 非托管代码之间字符串的混合编程