Page 1 of 1

Problem with "initialize-instance"

Posted: Sun Nov 11, 2012 9:03 am
by abvgdeika
I try to experiment with classes in LISP and I stuck at the following problem already at a very beginning level.
I define two classes "BIg" and "Small" so that "Small" is a subclass of "Big":

Code: Select all

(defclass Big () 
  ( (my-value  :accessor my-value)
   ))

(defclass Small (Big)  () )
For the class "Small" I define an after-method of initialization:

Code: Select all

(defmethod initialize-instance :after ( (item Small) &key )
  (setf (my-value item) 13)
  )
At this stage there is no problem with creating instances of both classes.
But then I execute the following code:

Code: Select all

(defgeneric Func (item) )

(defmethod initialize-instance :after ( (item Big) &key )
  (setf (my-value item) (Func 666) )
  )
Clearly, there are no methods defined for a new generic function Func and an attempt to create an instance of the class "Big" leads to errors.
But by some reason attempts to create instances of the class "Small", e.g. calls

Code: Select all

(setf x (make-instance 'Small ) )
also give the same error message saying that (Func 666) cannot be computed!
My question is: why "initialize-instance" chooses method for the class "Big" even when an instance of the class "Small" is created?

Re: Problem with "initialize-instance"

Posted: Sun Nov 11, 2012 2:28 pm
by Paul
abvgdeika wrote:But by some reason attempts to create instances of the class "Small", e.g. calls

Code: Select all

(setf x (make-instance 'Small ) )
also give the same error message saying that (Func 666) cannot be computed!
My question is: why "initialize-instance" chooses method for the class "Big" even when an instance of the class "Small" is created?
A "Small" is a "Big" (Small is a subclass of Big)!

Re: Problem with "initialize-instance"

Posted: Sun Nov 11, 2012 2:40 pm
by abvgdeika
Situation is not so easy. The code

Code: Select all

(defmethod initialize-instance :after ( (item Big) &key )
  (setf (my-value item) 666 )
  )

(setf x (make-instance 'Small ) )
results in creating an instance "x" having my-value 13, as expected since the method of initialization for the class "Small" is applied.
But replacing 666 by (Func 666) in the method definition for the class "Big" resuts in error even when creating instances of "Small".
Can someone explain this?

Re: Problem with "initialize-instance"

Posted: Sun Nov 11, 2012 5:38 pm
by Paul
abvgdeika wrote:Situation is not so easy. The code

Code: Select all

(defmethod initialize-instance :after ( (item Big) &key )
  (setf (my-value item) 666 )
  )

(setf x (make-instance 'Small ) )
results in creating an instance "x" having my-value 13, as expected since the method of initialization for the class "Small" is applied.
But replacing 666 by (Func 666) in the method definition for the class "Big" resuts in error even when creating instances of "Small".
Can someone explain this?
I just did -- a Small is a Big. The initalize-instance function consists of _all_ the relevant :before methods, the "main" method, and _all_ the relevant :after methods -- the method you defined for a Big is also relevant to a Small, since a Small is a Big, therefore the :after method runs and you get your error. The order is most-relevant to least-relevant on the way in (:before methods), and least-relevant to most-relevant on the way out (:after methods), so the :after method for Big runs before the :after method for Small -- it sets the slot to 666 and then sets it to 13 -- which is why you see 13 in the resulting object if you fix the error in the Big method.

Make the initialize-instance methods print something, so you can see what's happening.

Re: Problem with "initialize-instance"

Posted: Sun Nov 11, 2012 11:25 pm
by abvgdeika
Thank you for the explanation! I would say this is not so economic behaviour of LISP processor,
to run first all available methods and then to choose appropriate result. It seems much more natural to choose first
appropriate method and then to run it.

Re: Problem with "initialize-instance"

Posted: Mon Nov 12, 2012 1:28 am
by Paul
abvgdeika wrote:Thank you for the explanation! I would say this is not so economic behaviour of LISP processor,
to run first all available methods and then to choose appropriate result. It seems much more natural to choose first
appropriate method and then to run it.
It doesn't run them and then choose a result; it only runs what it needs to...before and after methods are meant to all run. It would be broken if it didn't run them all, since they all contribute to the meaning of the method. E.g., what would happen if you defined a (main; not before/after/etc.) method on Big but not on Small (so you intend for the Big method to run on smalls), and that method relies on some initialization done in a :before method (defined on Bigs, of course). If you wrote a :before method for Small that didn't duplicate that initialization, creating a Small would error out -- it has the run the :before method for Small, then the :before method for Big, then the main method, then the :after method for Big (if there is one), then the :after method for Small in order to ensure everything that needs to get done gets done. (There's also an :around method; if that exists, it gets called instead of the before/main/after sequence -- it's expected to use call-next-method to (eventually) invoke those).