【问题标题】:Racket macro for wrapping getter function around identifier用于将 getter 函数包装在标识符周围的球拍宏
【发布时间】:2017-06-27 21:40:35
【问题描述】:

我想在标识符周围包装一个 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 的新标识符并将其包含在文字列表中,但我不知道如何实现。

【问题讨论】:

    标签: macros racket wrapper setter getter


    【解决方案1】:

    您链接到的示例与上一节中的代码一起使用 定义了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,而不是尝试遵循指南中的原始示例。只是因为。

    【讨论】:

    • 感谢您的回答!我不知道这是否是故意的,但所有使用 (generate-accessors ...) “创建”的标识符都共享 private-val 的值。理想情况下,我想在模板中写一些类似“do-not-expand #'(get-val id)”的东西,这对我来说似乎容易多了。 (顺便说一句,你的“对宏的恐惧”读起来很棒,真的很喜欢自下而上的方法)。
    • 实际上generate-identifiers 将使用你给它的一对访问器的私有值。例如。上面的get-val/put-val! 有自己的值,不同于custom-get/custom-set!。现在,您可以让宏为每个标识符生成一对新的访问器和私有值。我可以用一个例子来更新答案。
    • 非常感谢!这正是我所需要的。尽管如此,作为一个宏的新手,我还是对模板也在同一个宏中展开感到好奇。这意味着你不能轻易地表达身份转换,我觉得这很奇怪。这是一个深思熟虑的设计选择还是有实施原因?
    • 其实说得通。在我的情况下 (syntax (get id)) id 是一个转换器绑定,而不是我定义的实际“变量”,所以扩展必须是无限的。很明显,我不知道我怎么可能不明白这一点。谢谢!
    猜你喜欢
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多