You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带&key和&body参数的宏未指定key参数的SBCL编译问题

SBCL编译宏时的函数对象转储错误原因分析

宏定义与问题场景

先看涉事宏的定义:

(defparameter *current-state* 'foo)

(defmacro when-state ((state &key (test #'eq)) &body body)
  `(when (funcall ,test *current-state* ,state)
     ,@body))

该宏在REPL环境下无论是否指定test关键字参数都能正常运行,但在顶层defun或lambda中使用时(如下示例),用SBCL编译文件会触发错误:

示例代码:

(defun foo ()
  (when-state ('foo)
    (format t "Foo")))

编译错误信息

编译时SBCL输出的错误内容:

; in: DEFUN FOO
;     (SENTO.FSM:WHEN-STATE ('SENTO.FSM::FOO)
;       (FORMAT T "Foo"))
; --> IF FUNCALL 
; ==>
;   1
;
; caught ERROR:
;   Objects of type COMPILED-FUNCTION can't be dumped into fasl files.
;
; note: The first argument never returns a value.

问题核心原因

问题出在宏的默认参数(test #'eq)上:

  • 当定义宏时,#'eq会被立即求值为当前环境下的已编译函数对象,而非符号引用。
  • 当宏展开到defun的代码中时,这个已编译的函数对象会被直接嵌入到生成的代码里。
  • SBCL的FASL编译文件无法存储已编译的函数对象,只能存储符号(加载时通过符号查找对应函数),因此编译时会报错。

而显式指定test参数时(比如(when-state ('foo :test #'eq))),#'eq是在函数foo的上下文里被引用,编译时会生成对符号eq的引用,而非直接嵌入函数对象,因此可以正常转储到FASL文件。

修复方法

把宏默认参数里的#'eq改成符号引用'eq,确保宏展开后引用的是符号而非已编译函数对象:

(defmacro when-state ((state &key (test 'eq)) &body body)
  `(when (funcall ,test *current-state* ,state)
     ,@body))

这样无论是否显式指定test参数,编译时都会处理为符号引用,避免无法转储的问题。

内容的提问来源于stack exchange,提问作者Manfred

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:08:09