您可以通过定义自己的define 版本来做到这一点,这会在编译时保存表达式,replace-plus-with-mul 可以稍后获取。
define/replacable 和 replace-plus-with-mul 两个宏必须使用 define-syntax 和 syntax-local-value 一起工作:
-
define/replacable 使用 define-syntax 将编译时信息与其定义的标识符相关联。
-
replace-plus-with-mul 使用 syntax-local-value 查找编译时信息。
First Pass,直接在define-syntax中保存一个函数
#lang racket
(require syntax/parse/define
(for-syntax syntax/transformer))
(define-syntax-parser define/replacable
[(_ name:id expr:expr)
#:with plus (datum->syntax #'name 'plus)
#:with mul (datum->syntax #'name 'mul)
#'(define-syntax name
;; Identifier Identifier -> Expression
;; Replaces plus and mul within the expr
;; with the two new identifiers passed to
;; the function
(lambda (plus mul)
(with-syntax ([plus plus] [mul mul])
#'expr)))])
(define-syntax-parser replace-plus-with-mul
[(_ name:id replacable:id)
(define replace (syntax-local-value #'replacable))
#`(define name #,(replace #'mul #'mul))])
有了这些定义,这个程序就可以工作了:
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define a 4)
(define b 2)
(define/replacable c (plus a b))
(replace-plus-with-mul d c) ;; (define d (mul a b))
(print d)
;=output> 8
但是,此示例中的c 不能用作普通表达式。它可以在replace-plus-with-mul 内使用,但只能在此范围内使用。这可以通过添加结构来解决。
第二遍,保存结构以便正常使用也可以
在第一个版本中,两个宏的通信方式如下:
-
define/replacable 使用 define-syntax 将编译时信息与其定义的标识符相关联。
-
replace-plus-with-mul 使用 syntax-local-value 来查找编译时信息。
但是,这不允许标识符具有正常行为。为此,我们需要这样的东西:
-
define/replacable 使用 define-syntax 将它定义的标识符与包含以下两者的编译时结构相关联:
-
replace-plus-with-mul 使用 syntax-local-value 查找该编译时结构,并从中获取 replace 行为
- 普通的 Racket 宏扩展器使用
syntax-local-value 来查找该编译时结构,并将其用作应用为宏的过程。因此,我们应该使结构具有#:property prop:procedure 并具有正常行为。
这个结构可以是这样的:
(begin-for-syntax
;; normal : Expression -> Expression
;; replace : Identifier Identifier -> Expression
(struct replacable-id [normal replace]
#:property prop:procedure (struct-field-index normal)))
现在define/replacable 宏应该生成一个define-syntax 来构造其中之一:
(define-syntax name
(replacable-id ???
(lambda (plus mul)
...what-we-had-before...)))
如果我们希望正常行为看起来像一个变量,我们可以使用来自syntax/transformer 的make-variable-like-transformer 填充??? 孔:
(require (for-syntax syntax/transformer))
(begin-for-syntax
;; Identifier -> [Expression -> Expression]
(define (make-var-like-transformer id)
(set!-transformer-procedure (make-variable-like-transformer id))))
那么define/replacable可以生成这样的东西:
(define normal-name expr)
(define-syntax name
(replacable-id (make-var-like-transformer #'normal-name)
(lambda (plus mul)
...what-we-had-before...)))
把它们放在一起:
#lang racket
(require syntax/parse/define
(for-syntax syntax/transformer))
(begin-for-syntax
;; Identifier -> [Expression -> Expression]
(define (make-var-like-transformer id)
(set!-transformer-procedure (make-variable-like-transformer id)))
;; normal : Expression -> Expression
;; replace : Identifier Identifier -> Expression
(struct replacable-id [normal replace]
#:property prop:procedure (struct-field-index normal)))
(define-syntax-parser define/replacable
[(_ name:id expr:expr)
#:with plus (datum->syntax #'name 'plus)
#:with mul (datum->syntax #'name 'mul)
#'(begin
(define normal-name expr)
(define-syntax name
(replacable-id (make-var-like-transformer #'normal-name)
(lambda (plus mul)
(with-syntax ([plus plus] [mul mul])
#'expr)))))])
(define-syntax-parser replace-plus-with-mul
[(_ name:id replacable:id)
(define value (syntax-local-value #'replacable))
(define replace (replacable-id-replace value))
#`(define name #,(replace #'mul #'mul))])
并尝试一下:
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define/replacable a 4)
(define/replacable b 2)
(define/replacable c (plus a b))
(replace-plus-with-mul d c) ;; (define d (mul a b))
(print d)
;=output> 8