(defclass test () ((slot :allocation :class :initform t)))
(defmethod initialize-instance :before ((test test) &key)
(format t "Slot boundp: ~A~%" (slot-boundp test 'slot)))
* (make-instance 'test)
Slot boundp: T
* (make-instance 'test)
Slot boundp: T
I just realized that because with ABCL (and contrary to the other
implementations I've tried), the result is NIL, and then T.
But now, I'm a little confused. Section 7.1.3 of CLHS says:
An :initform form is used to initialize a slot only if no
initialization argument associated with that slot is given as an
argument to make-instance or is defaulted by :default-initargs.
So after re-reading this (the "only" part notably), it now appears to me
somewhat abusive to initialize shared slots earlier than at first
instance creation time (like most lisps seem to do), because there's no
way to know, at that time, whether the fist call to make-instance will
provide an initarg.
WDYT?
Related to this is a question of style as well. I rarely use shared
slots, but until now, I thought of it as a convenient way to encapsulate
information that's supposed to be used by instances only (including the
first one). Maybe this is not a good idea after all...
--
Resistance is futile. You will be jazzimilated.
Scientific site: http://www.lrde.epita.fr/~didier
Music (Jazz) site: http://www.didierverna.com
> So after re-reading this (the "only" part notably), it now appears to me
> somewhat abusive to initialize shared slots earlier than at first
> instance creation time (like most lisps seem to do), because there's no
> way to know, at that time, whether the fist call to make-instance will
> provide an initarg.
I don't think that's what you're testing here. When the before method
on initialize-instance is called, make-instance has already been called
at that point, so it is already clear whether an initarg was provided I
think.
Something like this tells you if the initform is ever evaluated, but not if
the result is used:
(defclass fungy ()
((x :allocation :class
:initform (progn
(format *debug-io* "~&x initform~%")
6)
:initarg :x)
(y :initform (progn
(format *debug-io* "~&y initform~%")
7)
:initarg :y)))
I suspect the answer is (a) complicated and (b) not well-specified.
> I don't think that's what you're testing here. When the before method
> on initialize-instance is called, make-instance has already been
> called at that point, so it is already clear whether an initarg was
> provided I think.
Sure. My test was to illustrate that ABCL instantiates shared slots
with an initform when the first instance is created (and what's more no
earlier than the call to the primary method of initialize-instance).
This is contrary to the other implementations I've tried (SBCL, CMUCL,
CLISP, ECL, ACL) which seem to do it when the class is finalized.
> Something like this tells you if the initform is ever evaluated, but
> not if the result is used:
This test gives this:
ABCL:
CL-USER(1): (defclass fungy () ...)
#<STANDARD-CLASS FUNGY {1AEE513}>
CL-USER(2): (make-instance 'fungy)
x initform
y initform
#<FUNGY {18C2354}>
SBCL:
* (defclass fungy () ...)
x initform
#<STANDARD-CLASS FUNGY>
* (make-instance 'fungy)
y initform
So it confirms that ABCL evaluates the initform later than the other
implementations. But now, consider this:
(defclass test ()
((slot :allocation :class :initform t :initarg :slot)))
(defmethod initialize-instance :before ((test test) &key)
(format t "Slot value: ~A~%" (slot-value test 'slot)))
Fresh SBCL:
CL-USER> (make-instance 'test)
Slot value: T
#<TEST {B8115E1}>
Fresh SBCL again:
CL-USER> (make-instance 'test :slot nil)
Slot value: T
#<TEST {B92AC89}>
So even in the case where you provide an initarg, the slot is
initialized to the initform value (at least until the primary method on
initialize-instance is called). This is what I now find a little abusive
wrt the CLHS portion I cited.
> I suspect the answer is (a) complicated and (b) not well-specified.
Right :-)
> So even in the case where you provide an initarg, the slot is
> initialized to the initform value (at least until the primary method on
> initialize-instance is called). This is what I now find a little abusive
> wrt the CLHS portion I cited.
Although it's not part of the spec, the (AMOP) MOP is quite interesting
here. Once finalize-inheritance has been called (which is "no later
than when the first instance of the class is allocated") then you can
call class-prototype to get a prototype instance of the class. But
it's explicitly not defined whether that instance has been initialized.
So that might be taken to imply that it either might or might not get
initialized at the point where finalize-inheritance is called, perhaps?
I reckon there are just a load of corners in the CLOS parts of the spec
where things are a bit vague. Probably making them not be vague would
be an absurd amount of work. There was some recent discussion here by
someone who was making an attempt to sort a lot of it out with regards
to threading issues.
> Related to this is a question of style as well. I rarely use shared
> slots, but until now, I thought of it as a convenient way to encapsulate
> information that's supposed to be used by instances only (including the
> first one). Maybe this is not a good idea after all...
I believe that shared slots in the CLOS sense (i.e. :allocation :class)
are really not very useful. Useful (at least for me) would be shared
slots which can depend on the respective subclass.
Nicolas