I want to use constant variables in case macro as "Common Lisp Recipes" book recommends.
Unfortunately it doesn't work in Clozure CL.
(defpackage #:foo
(:use #:cl))
(in-package #:foo)
(defconstant +one+ 1)
(defconstant +two+ 2)
(defun lol (gg)
(ecase gg
(#.+one+ :one)
(#.+two+ :two)))
This code fails to load.
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.
The code works fine in SBCL. Why doesn't it work in CCL?
I am using 64 bit Clozure CL 1.12 on macOS.
CCL will happily load the source of this file for me, and I believe that any CL should do so.
What it won't do, and what I would be surprised if any CL will do, is compile it. It won't compile it because defconstant
doesn't define the constant at compile time. That means that, when lol
is compiled, there is a reference to a not-yet-defined variable.
If you want to treat constants like this you need to make sure the variables are defined at compile time. There are two ways of doing this:
Firstly you can just add suitable eval-when
ery, after which the relevant chunk of source will be:
(eval-when (:compile-toplevel :load-toplevel :execute)
(defconstant +one+ 1)
(defconstant +two+ 2))
(defun lol (gg)
(ecase gg
(#.+one+ :one)
(#.+two+ :two)))
The second is to put the constants in their own file which is compiled and loaded before they are used. Typically that's managed with a system-definition facility like ASDF.
Note: I believe that any CL should be able to load the source because, I think, even compiler-only implementations are required, when loading source code files, to treat them a form at a time: I don't think it is legal to turn (load "foo.lisp")
into (load (compile-file "foo.lisp"))
in other words. I might be wrong about that however: it's a long time since I read the spec that forensically.