0

It is said, only special variables in Common Lisp can be unbound. For all lexical variables the default value is nil. I thought that class slots exist in something like closure, but obviously they don't.

If I define a CLOS slots without :initform parameter (in hope that they will be bound to nil anyway) and do not supply values when creating instance, I'll get an instance with unbound slots. Why it is so? It's not handy.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Mark Karpov
  • 7,499
  • 2
  • 27
  • 62
  • 2
    "It is said". Where is that said? "in hope that they will be bound to nil anyway" - why would you hope for something, while the Common Lisp specification says something different? 'hoping' is not really a useful tool when dealing with programming languages. Having unbound slots allows for example to detect whether they are initialized or not. – Rainer Joswig Jun 22 '14 at 08:08
  • @RainerJoswig, You're perfectly right about 'hoping' ;-) It was just a bit of practical exploration of the language features before reading any docs. – Mark Karpov Jun 22 '14 at 08:28
  • See: http://www.gigamonkeys.com/book/object-reorientation-classes.html – Rainer Joswig Jun 22 '14 at 08:30
  • Not just slots and special variables, but symbols can be unbound as functions. See [fmakunbound](http://www.lispworks.com/documentation/HyperSpec/Body/f_fmakun.htm), and [makunbound](http://www.lispworks.com/documentation/HyperSpec/Body/f_makunb.htm#makunbound). Note that a symbol doesn't have to have a special declaration to have a symbol-value, too. – Joshua Taylor Jun 22 '14 at 13:16

2 Answers2

8

It is very handy for things like computing slot values on demand, where the value may sometimes be nil. When accessing an unbound slot, CLOS normally calls the SLOT-UNBOUND generic function, which signals an error. Instead of an error, though, you can specialize SLOT-UNBOUND to compute and store the value on demand. Subsequent accesses will use the slot value directly, and you can flush the slot "cache" by using SLOT-MAKUNBOUND.

You could do this with a sentinel "not-bound" value of some sort, but it's very handy to have the functionality built-in instead.

Example of slot-unbound use:

(defclass foo ()
  ((bar :accessor bar)
   (baz :accessor baz)))

(defmethod slot-unbound (class (instance foo) slot-name)
  (declare (ignorable class))
  (setf (slot-value instance slot-name) nil))

In action:

CL-USER> (defparameter *foo* (make-instance 'foo))
*FOO*
CL-USER> (bar *foo*)
NIL
CL-USER> (setf (baz *foo*) (not (baz *foo*)))
T
Xach
  • 11,774
  • 37
  • 38
5

CLOS instances are not closures

CLOS instances are usually not implemented as closures. That would be difficult. They are some data structure with something like a vector for the slots. Similar to Common Lisp's structures. A difference between CLOS instances and structures complicates it: it is possible for CLOS instances to change the number of slots at runtime and one can change the class of a CLOS instance at runtime.

Making sure slots have a NIL value

With some advanced CLOS you can make sure that slots have a NIL value. Note that the functions in the package CLOS in my example may be in another package in your CL.

This functions looks over all slots of an instance. If a slot is unbound, it gets set to NIL.

(defun set-all-unbound-slots (instance &optional (value nil))
  (let ((class (class-of instance)))
    (clos:finalize-inheritance class)
    (loop for slot in (clos:class-slots class)
          for name = (clos:slot-definition-name slot)
          unless (slot-boundp instance name)
          do (setf (slot-value instance name) value))
    instance))

We make a mixin class:

(defclass set-unbound-slots-mixin () ())

The fix will be run after initializing the object.

(defmethod initialize-instance :after ((i set-unbound-slots-mixin) &rest initargs)
  (set-all-unbound-slots i nil))

Example:

(defclass c1 (set-unbound-slots-mixin)
  ((a :initform 'something)
   b
   c))


CL-USER 1 > (describe (make-instance 'c1))

#<C1 4020092AEB> is a C1
A      SOMETHING
B      NIL
C      NIL
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346