I'll bore you just one more time with this: When executing (test) as defined
in the following code, Lisp claimed that the function #:TEST_DIALOG is undefined.
(defun test()
(test_dialog))
(in-package :clausbrod.de)
(use-package :oli)
(sd-defdialog 'test_dialog
:ok-action '(display "test_dialog"))
In
part 3 of this mini-series,
we figured out that the
#: prefix indicates an
uninterned symbol - and now
we can solve the puzzle!
Earlier, I had indicated that
sd-defdialog
automatically exports dialog
names into the default package. To perform this trick, somewhere in the bowels of
the
sd-defdialog
macro, the following code is generated and executed:
(shadowing-import ',name :cl-user) ;; import dialog name into cl-user package
(export ',name) ;; export dialog name in current package
(import ',name :oli) ;; import dialog name into oli package
(export ',name :oli) ;; export dialog name from the oli package
As a consequence, the dialog's name is now visible in three packages:
- The default package (
cl-user
)
- Our Lisp API package (
oli
)
- The package in which the dialog was defined (here:
clausbrod.de
)
This is quite convenient for CoCreate Modeling users - typically
mechanical engineers, not Lisp programmers. They don't want to deal with the
intricacies of Lisp's package handling, but instead simply assume that the
command (dialog) will be at their disposal whenever they need it.
Let's look up what the Common Lisp standard has to say on
shadowing-import:
shadowing-import inserts each of symbols into package as an internal symbol, regardless of whether another symbol of the same name is shadowed by this action. If a different symbol of the same name is already present in package, that symbol is first uninterned from package.
That's our answer! With this newly-acquired knowledge, let's go through our
code example one more and final time:
(defun test()
(test_dialog))
Upon loading this code, the Lisp reader will intern a symbol
called
test_dialog
into the current (default) package. As
test_dialog
has not
been defined yet, the symbol
test_dialog
does not have a value; it's just
a placeholder for things to come.
(in-package :clausbrod.de)
(use-package :oli)
We're no longer in the default package, and can freely use
oli:sd-defdialog
without
a package prefix.
(sd-defdialog 'test_dialog
:ok-action '(display "test_dialog"))
sd-defdialog
performs
(shadowing-import 'test_dialog :cl-user),
thereby shadowing (hiding)
and uninterning the previously interned
test_dialog
symbol.
Until we re-evaluate the definition for
(test)
, it will still refer to the
old definition of the symbol
test_dialog
, which - by now - is a) still without
a value and b) uninterned, i.e. homeless.
Lessons learned:
- Pay attention to the exact wording of Lisp error messages.
- The Common Lisp standard is your friend.
- Those Lisp package problems can be pesky critters.
The good news: If you follow a few rules of thumb, you'll probably never run into
complex package problems like this. One such simple rule is to define your
functions first before referring to them. So in our code example, defining
the dialog first before loading/defining the
(test)
function would have saved
us all that hassle.
Phew.