The symbol-package function can be used to locate the original package in which a symbol is defined. Because it is possible to import symbols into other packages, it may sometimes not be clear what the origin is of a symbol being used. Here is a transcript that demonstrates the use of symbol-package. In it, we create a package, and a function in that package. We then create a second package and import the symbol corresponding to the function. Outside of either package, we can invoke the function in the second package, but symbol-package reveals that it is actually found in the first.
CL-USER> (defpackage :base (:use :common-lisp))
CL-USER> (in-package :base)
BASE> (defun myfunc () (format t "hi~%"))
BASE> (defpackage :derived (:use :common-lisp))
BASE> (in-package :derived)
DERIVED> (import 'base::myfunc)
DERIVED> (in-package :cl-user)
CL-USER> (symbol-package 'derived::myfunc)
Our next obscure Lisp feature is the symbol-macrolet special operator. This operator produces a context in which one or more symbols can be rewritten prior to evaluation. While let, and similar operators, can shadow it, outside of that exception, the symbols are substituted according to the expansions supplied.
Here is some code to demonstrate the behaviour:
(defun demonstrate ()
(let ((replacement-1 'my-new-fcn-1))
"(my-fcn-1 my-fcn-2): ~A~%"
(list my-fcn-1 my-fcn-2))
(format t "Now, binding to shadow my-fcn-2~%")
(multiple-value-bind (result my-fcn-2)
(floor 10.4 1.2)
"(my-fcn-1 my-fcn-2): ~A~%"
(list my-fcn-1 my-fcn-2))))))
(my-fcn-1 my-fcn-2): (MY-NEW-FCN-1 RPL-2)
Now, binding to shadow my-fcn-2
(my-fcn-1 my-fcn-2): (MY-NEW-FCN-1 0.79999924)
We’ve arrived now at symbol-function. This accessor has a subset of the functionality of fdefinition, so we’re not going to say much about it here. It can be used to read or replace the function bound to a symbol. Whether to use symbol-function or fdefinition in that context is really up to the programmer.
Our next function of interest is sxhash. This function provides an implementation-defined way to obtain a non-negative integer from a Lisp object. The programmer can then use this in his or her own implementation of a hash table, or some similar application that requires a similar sparse mapping to positive integers. Note that the standard-defined hash tables produced with make-hash-table cannot be made to use a custom hashing function, however check your implementation, they may have extended it to allow this. SBCL 1.1.14, for instance, defines a &key argument to make-hash-table, hash-function, which allows a user-supplied function to replace the normal internal one. It’s also worth noting that SBCL 1.1.14 uses sxhash internally when no hash-function is supplied by the programmer. Finally, that version of SBCL also defines an internal implementation psxhash function, not visible to the programmer, for use when the hash-table is constructed with an equalp test instead of the default equal.
As noted above, the sxhash function is implementation defined. The implementation is encouraged to make a good effort to return values distributed over the set of non-negative fixnums.
There are a few things to note about the implementation of sxhash, they are described in the CLHS.
- If two objects are equal, they must have the same hash returned by sxhash.
- Simple objects of type “bit vectors, characters, conses, numbers, pathnames, strings, or symbols” (to quote the CLHS), must return the same hash even from objects resident in different Lisp images. This means, for instance, that a hash computed at compile time in the image that compiles some Lisp code must be the same as a hash recomputed on a similar object in a different image that loads the compiled code. If this seems mysterious, you might want to review the earlier article on make-load-form.
- The hash code of an object must not change as long as that object is unchanged, or only changed in such a way that the object remains equal to its earlier form.
- As noted above, sxhash should me a good effort to produce good, spread-out values.
- The sxhash function should always terminate, even when presented with circular objects.
Our next obscure function is subtypep. This function allows the program to determine whether one type is a subtype of a second type. The return values depend on the actual types available in the implementation, as some implementations may not support all available types, and may alias one type to another, in which case they are subtypes of one another.
The subtypep function can also determine whether a class is derived from another class. All subclasses of a class “C”, even through multiple levels or through multiple inheritance with an unrelated class, are subtypes of class “C”.
This function is somewhat esoteric in its uses. It can be used to ensure that a passed parameter is of the correct type for the actions that are about to be attempted on it, or in other branching contexts. Contexts where you might use typecase could possible be amenable to the use of subtypep.