如何修复Common Lisp宏的符号内联跨包问题?
解决Common Lisp跨包宏定义函数的包归属问题
你遇到的这个问题是Common Lisp宏展开的典型陷阱:宏模板里的符号默认是在宏定义的包(也就是包a)中解析的,而不是你调用宏时的当前包(b或c)。所以当你在包b里调用makefun时,宏展开出来的fun会被解析成A::FUN,自然在包b里调用(fun)会找不到这个函数。
解决方案:在宏展开时动态绑定当前包的符号
我们需要修改makefun宏,让它在展开时,把fun符号创建在调用宏的当前包里。具体来说,就是用intern函数在当前包中生成FUN符号,再把这个符号代入到defun模板里。
修改后的完整代码如下:
(defpackage #:a (:use #:cl) (:export makefun)) (in-package #:a) (defmacro makefun (&optional (name (package-name *package*))) ;; 关键:在调用宏的当前包中创建FUN符号 (let ((fun-symbol (intern "FUN" *package*))) `(defun ,fun-symbol () ,(format nil "hello ~a" name)))) (defpackage #:b (:use #:cl #:a)) (in-package #:b) (macroexpand-1 '(makefun)) ; 现在展开为 (DEFUN B::FUN () "hello B") (makefun) (fun) ; 执行成功,返回"hello B" (defpackage #:c (:use #:cl #:a)) (in-package #:c) (macroexpand-1 '(makefun)) ; 展开为 (DEFUN C::FUN () "hello C") (makefun) (fun) ; 执行成功,返回"hello C"
原理说明
*package*在宏展开阶段的值是调用宏的当前包(比如你在包b里调用makefun时,*package*就是#<PACKAGE "B">),而不是宏定义所在的包a。(intern "FUN" *package*)会把FUN这个符号注册到当前调用宏的包中,如果符号不存在就创建它。- 最后把这个动态生成的符号代入到
defun的模板里,这样展开后的defun就会在当前包定义fun函数了。
内容的提问来源于stack exchange,提问作者Sunder
相关产品推荐
相关产品推荐

