Clozure Common Lisp 中的 Sharpsign 点宏

Sharpsign dot macro in Clozure Common Lisp

我想按照“Common Lisp Recipes”一书的建议在 case 宏中使用常量变量。

不幸的是,它在 Clozure CL 中不起作用。

(defpackage #:foo
  (:use #:cl))

(in-package #:foo)

(defconstant +one+ 1)
(defconstant +two+ 2)

(defun lol (gg)
  (ecase gg
    (#.+one+ :one)
    (#.+two+ :two)))

此代码加载失败。

 Unbound variable: FOO::+ONE+
   [Condition of type UNBOUND-VARIABLE]

Restarts:
 0: [CONTINUE] Retry getting the value of FOO::+ONE+.
 1: [USE-VALUE] Specify a value of FOO::+ONE+ to use this time.
 2: [STORE-VALUE] Specify a value of FOO::+ONE+ to store and use.

该代码在 SBCL 中运行良好。为什么它在 CCL 中不起作用?

我在 macOS 上使用 64 位 Clozure CL 1.12。

CCL 会很乐意为我加载此文件的 source,我相信任何 CL 都应该这样做。

它不会做的,如果有任何 CL 会做,我会感到惊讶的是,编译它。它不会编译它,因为 defconstant 在编译时没有定义常量。这意味着,当编译 lol 时,会引用一个尚未定义的变量。

如果你想像这样处理常量,你需要确保变量在编译时定义的。有两种方法:

首先你可以添加合适的eval-whenery,之后相关的源代码块将是:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defconstant +one+ 1)
  (defconstant +two+ 2))

(defun lol (gg)
  (ecase gg
    (#.+one+ :one)
    (#.+two+ :two)))

第二种是将常量放在自己的文件中,在使用前编译并加载。通常,这是使用 ASDF 等系统定义工具进行管理的。


注意:我相信任何 CL 都应该能够加载源代码,因为我认为,在加载源代码文件时,甚至需要仅编译器的实现,一次将它们视为一种形式:我不换句话说,不认为将 (load "foo.lisp") 变成 (load (compile-file "foo.lisp")) 是合法的。然而,我对此可能是错误的:我已经有很长时间没有从法庭上阅读规范了。