用于围绕标识符包装 getter 函数的 Racket 宏
Racket macro for wrapping getter function around identifier
我想用一个 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))导致无限扩展。
有没有一种直接的方法来切断模板表达式的递归?我还考虑过生成绑定到 id 的新标识符并将其包含在文字列表中,但我不知道如何完成此操作。
您 link 使用上一节中的代码的示例
定义 get-val
和 put-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
此外,让我们用另一对访问器来练习它,它显示了
像 "logging":
这样的副作用
(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
)。 (你可以简化它来硬编码 println
或 log-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
而不是尝试遵循 指南 中的原始示例。只是因为.
我想用一个 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))导致无限扩展。 有没有一种直接的方法来切断模板表达式的递归?我还考虑过生成绑定到 id 的新标识符并将其包含在文字列表中,但我不知道如何完成此操作。
您 link 使用上一节中的代码的示例
定义 get-val
和 put-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
此外,让我们用另一对访问器来练习它,它显示了 像 "logging":
这样的副作用(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
)。 (你可以简化它来硬编码 println
或 log-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
而不是尝试遵循 指南 中的原始示例。只是因为.