【发布时间】:2016-02-26 12:53:54
【问题描述】:
在下面的文件中,宏m2 定义了一个标识符foo,并通过syntax-local-introduce 将其提供给用户代码。随后扩展 m1 时,(free-identifier=? #'m2-id #'user-id) 的计算结果与预期一样为 #t。
#lang racket
(define-syntax (m1 stx)
(syntax-case stx ()
[(_ m2-id user-id)
#`#,(free-identifier=? #'m2-id #'user-id)]))
(define-syntax (m2 stx)
(syntax-case stx ()
[(_ user-id)
#`(begin (define (foo) 1)
(m1 foo #,(syntax-local-introduce #'user-id)))]))
(m2 foo) ;; => #t
(m2 foo) ;; => #t
但是,如果我将两个宏包装在一个模块中,(free-identifier=? #'m2-id #'user-id) 的计算结果为 #f。
#lang racket
(module m racket
(define-syntax (m1 stx)
(syntax-case stx ()
[(_ m2-id user-id)
#`#,(free-identifier=? #'m2-id #'user-id)]))
(define-syntax (m2 stx)
(syntax-case stx ()
[(_ user-id)
#`(begin (define (foo) 1)
(m1 foo #,(syntax-local-introduce #'user-id)))]))
(provide m2))
(require 'm)
(m2 foo) ;; => #f
(m2 foo) ;; => #f
为什么会这样?
当宏定义在单独的模块中时,如何解决这个问题并获得第一个示例的行为?
请注意,我希望能够多次执行宏,因此不能使用(define (#,(datum->syntax stx 'foo)) 1),因为这会在调用宏时导致定义冲突。
我尝试使用((make-syntax-delta-introducer #'foo #'user-id) #'user-id) 代替#,(syntax-local-introduce #'user-id),但它也不起作用。
我正在使用的实际代码
这是(伪代码)我正在编写的实际代码:
(define-syntax (define* stx)
... expand types, uses free-identifier=? ...)
(define-syntax (define-function-group stx)
(syntax-parse stx
[(_ ((~literal defun) (f-name arg ...) ret-type body) ...)
#`(begin (define-type-expander (return-type-of stx)
(syntax-parse stx
[(_ (~literal f-name)) #'ret-type] ...))
(define* (f-name arg ...) : ret-type
body) ...)]))
;; defines a type expander "return-type-of"
(define-function-group
(defun (f1) (Listof String) '("a" "b"))
(defun (f2 [a : (return-type-of f1)] (length '("a" "b")))))
;; defines another type expander "return-type-of"
(define-function-group etc.)
define-type-expander 来自我的类型扩展器库,其工作方式类似于匹配扩展器(我应该在某个时候将其打包,但仍需要一些工作)。 define*(对应于上面的m1)是扩展类型的define* 的变体,它在某些时候使用free-identifier=? 来找到正确的类型扩展器定义。 define-function-group(对应于上面的m2)允许使用(return-type-of some-function-name) 引用另一个函数的返回类型。问题是每次调用define-function-group 都会引入return-type-of 的新副本,因此它需要有宏的作用域以避免冲突。
【问题讨论】:
-
我承认,我对这试图解决的问题感到非常困惑。您希望传入的标识符是
free-identifier=?到另一个不相关的标识符?我不明白这段代码的目的是什么,我闻到了潜在的XY problem。 -
@AlexisKing 我为我的真实用例添加了伪代码。我知道
syntax-parameterize在这里会是一个很好的解决方案,但它会导致其他问题(我隐约怀疑我的类型扩展库中有一些怪癖)。