[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

CLOS constructor in precedence order

Otto Diesenbacher

8/7/2015 9:04:00 PM

Dear Lispers,

"initialize-instance :after" is mentioned (f.e. in PCL) as a kind of
constructor for classes. But as far as I understand, :after methods
are called in reverse order of the precedence list.

How could I establish a constructor, that is called in the "natural"
order of the precedence list, but after the primary
initialize-instance method (that does all the initarg/initform magic).

best regards and thanks for any advice
okflo
16 Answers

taruss

8/8/2015 12:14:00 AM

0

On Friday, August 7, 2015 at 2:03:59 PM UTC-7, ok...@diesenbacher.net wrote:
> Dear Lispers,
>
> "initialize-instance :after" is mentioned (f.e. in PCL) as a kind of
> constructor for classes. But as far as I understand, :after methods
> are called in reverse order of the precedence list.
>
> How could I establish a constructor, that is called in the "natural"
> order of the precedence list, but after the primary
> initialize-instance method (that does all the initarg/initform magic).

What about

(defmethod initialize-instance ((instance my-class) &rest initargs)
(call-next-method)
;; Add your code here:
)

Kaz Kylheku

8/8/2015 1:15:00 AM

0

On 2015-08-07, okflo@diesenbacher.net <okflo@diesenbacher.net> wrote:
> Dear Lispers,
>
> "initialize-instance :after" is mentioned (f.e. in PCL) as a kind of
> constructor for classes. But as far as I understand, :after methods
> are called in reverse order of the precedence list.
>
> How could I establish a constructor, that is called in the "natural"
> order of the precedence list, but after the primary
> initialize-instance method (that does all the initarg/initform magic).

Implement a generic function called construct.
Give it a primary method specialized to t, so it
can be called on any object. This method does nothing.

For all the classes you care about, implement only a primary
initialize-instance method. Have this method call
the construct generic function.

Lastly, add :before methods on construct as needed.

Pascal J. Bourguignon

8/8/2015 2:23:00 AM

0

okflo@diesenbacher.net writes:

> Dear Lispers,
>
> "initialize-instance :after" is mentioned (f.e. in PCL) as a kind of
> constructor for classes. But as far as I understand, :after methods
> are called in reverse order of the precedence list.
>
> How could I establish a constructor, that is called in the "natural"
> order of the precedence list, but after the primary
> initialize-instance method (that does all the initarg/initform magic).

You're asking for trouble.

What will you do, in the initialize-instance :after methods, if not
_using_ or _modifying_ the instance? Then you will be _using_ or
_modifying_ an instance that is not finished initializaling! This will
break big time.


--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

Kaz Kylheku

8/8/2015 5:57:00 AM

0

On 2015-08-08, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
> okflo@diesenbacher.net writes:
>
>> Dear Lispers,
>>
>> "initialize-instance :after" is mentioned (f.e. in PCL) as a kind of
>> constructor for classes. But as far as I understand, :after methods
>> are called in reverse order of the precedence list.
>>
>> How could I establish a constructor, that is called in the "natural"
>> order of the precedence list, but after the primary
>> initialize-instance method (that does all the initarg/initform magic).
>
> You're asking for trouble.
>
> What will you do, in the initialize-instance :after methods, if not
> _using_ or _modifying_ the instance? Then you will be _using_ or
> _modifying_ an instance that is not finished initializaling! This will
> break big time.

Using and modifying partially constructed or destroyed objects is exactly what
happens in C++, and is the source of silly bugs like starting a thread in the
constructor of a base class which then runs on the object before the
constructor has finished constructing the more derived parts of the object.

Pascal J. Bourguignon

8/8/2015 6:49:00 AM

0

Kaz Kylheku <kaz@kylheku.com> writes:

> On 2015-08-08, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
>> okflo@diesenbacher.net writes:
>>
>>> Dear Lispers,
>>>
>>> "initialize-instance :after" is mentioned (f.e. in PCL) as a kind of
>>> constructor for classes. But as far as I understand, :after methods
>>> are called in reverse order of the precedence list.
>>>
>>> How could I establish a constructor, that is called in the "natural"
>>> order of the precedence list, but after the primary
>>> initialize-instance method (that does all the initarg/initform magic).
>>
>> You're asking for trouble.
>>
>> What will you do, in the initialize-instance :after methods, if not
>> _using_ or _modifying_ the instance? Then you will be _using_ or
>> _modifying_ an instance that is not finished initializaling! This will
>> break big time.
>
> Using and modifying partially constructed or destroyed objects is exactly what
> happens in C++, and is the source of silly bugs like starting a thread in the
> constructor of a base class which then runs on the object before the
> constructor has finished constructing the more derived parts of the object.

All right, it doesn't really matter in what order your
initialize-instance method run, since in any case, you must ensure that
you don't do anything requiring initialization that has not been done.

So the question is how to ensure that most easily and safely?
What order of the :after methods will let you avoid breakage more
easily?


And indeed, as Kay says, if you call in a initialize-instance method
which ever it is, a method on the object being initialized, you better
have to specific that generic function so that no specialized method
will ever have to work the object half initialized.

Clearly, this kind of operations (like this thread starting), should not
be done in the constructor or initialize-instance method, and you need a
protocol where you first initialize completely the instance, and then
call a start/open method on it. Ie. confirmation that RIAA is dumb.

(Of course, if you take the axiom that RIAA is good, then you must
conclude that inheriting is bad).


In any case, always check this diagram to find on what kind of method
you should put your code:

https://pupeno.files.wordpress.com/2011/08/common-lisp-method-compo...

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

Kenneth Tilton

8/9/2015 1:59:00 AM

0

On Friday, August 7, 2015 at 5:03:59 PM UTC-4, ok...@diesenbacher.net wrote:
> Dear Lispers,
>
> "initialize-instance :after" is mentioned (f.e. in PCL) as a kind of
> constructor for classes. But as far as I understand, :after methods
> are called in reverse order of the precedence list.

You make that sound like a problem. Have you thought it through? (Hint: no, you have npt.)

>
> How could I establish a constructor, that is called in the "natural"
> order of the precedence list, but after the primary
> initialize-instance method (that does all the initarg/initform magic).
>
> best regards and thanks for any advice
> okflo

You are hopelessly confused. I note you put the word natural in quotes, which I grant is an intellectually honest concession that you do not understand the word. Full marks for that.

Your first problem is that you are fighting the language, trying to interweave the i-i precedence with a different precedence. eg, you could call my-constructor in an i-i after method, but then what if you code a subclass with an i-i after? If you meant my-constructor to have the last word on initialization (you do, based on your wish for it to run after all afters) it will not.

Quick, before Costanza comes along to tell you how to succeed with your insanity, tell us what you are trying to achieve. Spoiler alert: we will then sort you out by helping you see what you are doing semantically, not by dicking with CLOS. And it will be vastly more helpful to you to achieve the former, because your new enlightenment will inform all the code you write.

-hk

okflo

8/9/2015 9:18:00 AM

0

His Kennyness <kentilton@gmail.com> writes:

> Quick, before Costanza comes along to tell you how to succeed with
> your insanity, tell us what you are trying to achieve. Spoiler alert:
> we will then sort you out by helping you see what you are doing
> semantically, not by dicking with CLOS. And it will be vastly more
> helpful to you to achieve the former, because your new enlightenment
> will inform all the code you write.

Dear taurus, Pascal, Kaz, Ken, ...

thanks for your hints, perhaps/likely - as some of you mentioned - my
approach is wrong.

Let me give you an example, what I want: I have a class /vehicle/
which should be initialized by the user with the number of wheels. The
constructor makes out of the number-of-wheels a description.

(defclass vehicle ()
((description
:accessor description)
(number-of-wheels
:initarg :number-of-wheels
:accessor number-of-wheels)))

(defmethod initialize-instance :after ((v vehicle) &key)
(setf (description v)
(format nil "This is a Vehicle with ~A wheels."
(number-of-wheels v))))

Now a have a following class /caterpillar/ (in my real use case the
"succession" is much longer). which should be initialized by the user
with the number of caterpillar-tracks. Its constructor should
calculate the number of wheels (which won't be provided by the user in
that case), and then it should call the constructor of /vehicle/ and
generate the description.

(defclass caterpillar (vehicle)
((number-of-caterpillar-tracks
:initarg :number-of-caterpillar-tracks
:accessor number-of-caterpillar-tracks)))

(defmethod initialize-instance :after ((v caterpillar) &key)
(setf (number-of-wheels v)
(* 16 (number-of-caterpillar-tracks v))))

But that will not work - as I expected - because
"initialize-instance :after of /vehicle/" is called before
"initialize-instance :after of /caterpillar/", because of the reverse
class precedence order of :after methods, so number-of-wheels will be
first unbound.

So that lead to my initial question - a way to call the constructors
in the class precedence order, but after the primary
initialize-instance that does all the initarg/initform magic.

The only solution I see at the moment is putting the "make-instance"
calls into their own function (make-vehicle, make-caterpillar, ...),
and that calls primary methods (with call-next-method) and does the
initializion. But I would really like to have all these things hidden
behind make-instance...

hmmm, missing the wood for the trees? Thanks for any hints...
okflo

Pascal J. Bourguignon

8/9/2015 10:07:00 AM

0

okflo@diesenbacher.net (Otto Diesenbacher-Reinmüller) writes:

> His Kennyness <kentilton@gmail.com> writes:
>
>> Quick, before Costanza comes along to tell you how to succeed with
>> your insanity, tell us what you are trying to achieve. Spoiler alert:
>> we will then sort you out by helping you see what you are doing
>> semantically, not by dicking with CLOS. And it will be vastly more
>> helpful to you to achieve the former, because your new enlightenment
>> will inform all the code you write.
>
> Dear taurus, Pascal, Kaz, Ken, ...
>
> thanks for your hints, perhaps/likely - as some of you mentioned - my
> approach is wrong.
>
> Let me give you an example, what I want: I have a class /vehicle/
> which should be initialized by the user with the number of wheels. The
> constructor makes out of the number-of-wheels a description.
>
> (defclass vehicle ()
> ((description
> :accessor description)
> (number-of-wheels
> :initarg :number-of-wheels
> :accessor number-of-wheels)))
>
> (defmethod initialize-instance :after ((v vehicle) &key)
> (setf (description v)
> (format nil "This is a Vehicle with ~A wheels."
> (number-of-wheels v))))
>
> Now a have a following class /caterpillar/ (in my real use case the
> "succession" is much longer). which should be initialized by the user
> with the number of caterpillar-tracks. Its constructor should
> calculate the number of wheels (which won't be provided by the user in
> that case), and then it should call the constructor of /vehicle/ and
> generate the description.

So you need a :before method.

> (defclass caterpillar (vehicle)
> ((number-of-caterpillar-tracks
> :initarg :number-of-caterpillar-tracks
> :accessor number-of-caterpillar-tracks)))
>
> (defmethod initialize-instance :after ((v caterpillar) &key)
> (setf (number-of-wheels v)
> (* 16 (number-of-caterpillar-tracks v))))

(defmethod initialize-instance :before ((v caterpillar) &key number-of-caterpillar-tracks &allow-other-keys)
(setf (number-of-wheels v)
(* 16 number-of-caterpillar-tracks)))

> hmmm, missing the wood for the trees? Thanks for any hints...

Also, check:
http://pupeno.com/2007/09/06/common-lisp-method-co...

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

Frank GOENNINGER

8/9/2015 10:35:00 AM

0

okflo@diesenbacher.net (Otto Diesenbacher-Reinmüller) writes:

> Dear taurus, Pascal, Kaz, Ken, ...
>
> thanks for your hints, perhaps/likely - as some of you mentioned - my
> approach is wrong.
>
> Let me give you an example, what I want: I have a class /vehicle/
> which should be initialized by the user with the number of wheels. The
> constructor makes out of the number-of-wheels a description.
>
> (defclass vehicle ()
> ((description
> :accessor description)
> (number-of-wheels
> :initarg :number-of-wheels
> :accessor number-of-wheels)))
>
> (defmethod initialize-instance :after ((v vehicle) &key)
> (setf (description v)
> (format nil "This is a Vehicle with ~A wheels."
> (number-of-wheels v))))
>
> Now a have a following class /caterpillar/ (in my real use case the
> "succession" is much longer). which should be initialized by the user
> with the number of caterpillar-tracks. Its constructor should
> calculate the number of wheels (which won't be provided by the user in
> that case), and then it should call the constructor of /vehicle/ and
> generate the description.
>
> (defclass caterpillar (vehicle)
> ((number-of-caterpillar-tracks
> :initarg :number-of-caterpillar-tracks
> :accessor number-of-caterpillar-tracks)))
>
> (defmethod initialize-instance :after ((v caterpillar) &key)
> (setf (number-of-wheels v)
> (* 16 (number-of-caterpillar-tracks v))))
>
> But that will not work - as I expected - because
> "initialize-instance :after of /vehicle/" is called before
> "initialize-instance :after of /caterpillar/", because of the reverse
> class precedence order of :after methods, so number-of-wheels will be
> first unbound.
>
> So that lead to my initial question - a way to call the constructors
> in the class precedence order, but after the primary
> initialize-instance that does all the initarg/initform magic.
>
> The only solution I see at the moment is putting the "make-instance"
> calls into their own function (make-vehicle, make-caterpillar, ...),
> and that calls primary methods (with call-next-method) and does the
> initializion. But I would really like to have all these things hidden
> behind make-instance...
>
> hmmm, missing the wood for the trees? Thanks for any hints...
> okflo

CELLS !!!!!!!! (Hey Kenny! :-D)

This is what data-flow oriented programming is for ...

(in-package :cl-user)

(eval-when (:load-toplevel :compile-toplevel :execute)
(ql:quickload :cells))

(use-package :cells)

(defmd vehicle ()
description
number-of-wheels
:description (c? (let ((number-of-wheels (^number-of-wheels)))
(when number-of-wheels
(format nil "This is a vehicle with ~A wheels." number-of-wheels)))))

(defobserver nr-of-wheels ()
(when new-value
(format *debug-io* "~&Number of wheels now: ~S." new-value)))

(defobserver description ()
(when new-value
(format *debug-io* "~&Description now: ~S." new-value)))

(defmd caterpillar (vehicle)
number-of-caterpillar-tracks
:number-of-wheels (c? (let ((number-of-caterpillar-tracks (^number-of-caterpillar-tracks)))
(when (and number-of-caterpillar-tracks
(typep number-of-caterpillar-tracks 'integer))
(* 16 number-of-caterpillar-tracks)))))

(defmethod print-object ((self vehicle) stream)
(print-unreadable-object (self stream :type t)
(format stream "~A" (description self))))

(defun cells-makes-you-happy ()
(let ((vehicle (make-instance 'vehicle :number-of-wheels (c-in 4)))
(caterpillar (make-instance 'caterpillar :number-of-caterpillar-tracks (c-in 2))))
(format *debug-io* "~&Vehicle: ~S." vehicle)
(format *debug-io* "~&Caterpillar: ~S." caterpillar)))


Gives:

CL-USER> (cells-makes-you-happy)

Description now: "This is a vehicle with 4 wheels.".
Description now: "This is a vehicle with 32 wheels.".
Vehicle: #<VEHICLE This is a vehicle with 4 wheels.>.
Caterpillar: #<CATERPILLAR This is a vehicle with 32 wheels.>.

Have fun!

Cheers
Frank

Madhu

8/9/2015 11:18:00 AM

0

* (Otto Diesenbacher-Reinmüller) <87614o4y72.fsf@news.diesenbacher.net> :
Wrote on Sun, 09 Aug 2015 11:18:25 +0200:

| Let me give you an example, what I want: I have a class /vehicle/
| which should be initialized by the user with the number of wheels. The
| constructor makes out of the number-of-wheels a description.
|
| (defclass vehicle ()
| ((description
| :accessor description)
| (number-of-wheels
| :initarg :number-of-wheels
| :accessor number-of-wheels)))
|
| (defmethod initialize-instance :after ((v vehicle) &key)
| (setf (description v)
| (format nil "This is a Vehicle with ~A wheels."
| (number-of-wheels v))))
|

This should probably be an AROUND method, as it encapsulates some
behaviour for ALL subclasses

(defmethod initialize-instance :around ((v vehicle) &key)
(call-next-method)
(setf (description v)
(format nil "This is a Vehicle with ~A wheels."
(number-of-wheels v))))

| Now a have a following class /caterpillar/ (in my real use case the
| "succession" is much longer). which should be initialized by the user
| with the number of caterpillar-tracks. Its constructor should
| calculate the number of wheels (which won't be provided by the user in
| that case), and then it should call the constructor of /vehicle/ and
| generate the description.
|
| (defclass caterpillar (vehicle)
| ((number-of-caterpillar-tracks
| :initarg :number-of-caterpillar-tracks
| :accessor number-of-caterpillar-tracks)))
|
| (defmethod initialize-instance :after ((v caterpillar) &key)
| (setf (number-of-wheels v)
| (* 16 (number-of-caterpillar-tracks v))))
|
| But that will not work - as I expected - because
| "initialize-instance :after of /vehicle/" is called before
| "initialize-instance :after of /caterpillar/", because of the reverse
| class precedence order of :after methods, so number-of-wheels will be
| first unbound.
|
| So that lead to my initial question - a way to call the constructors
| in the class precedence order, but after the primary
| initialize-instance that does all the initarg/initform magic.

One point missing in this example is that the initialize-instance
methods have access to the keyword arguments and can instantiate the
objects appropriately, this gives a means to model the initialization
steps in another way. Again the CLOS method combination makes sense. [I
remember having wanted this order during initialization, and I remember
having written a method combination for doing this, but I it looks I
deleted the code when I realized I it was the wrong idea]

| The only solution I see at the moment is putting the "make-instance"
| calls into their own function (make-vehicle, make-caterpillar, ...),
| and that calls primary methods (with call-next-method) and does the
| initializion. But I would really like to have all these things hidden
| behind make-instance...

Doesn't the around method accomodate for modeling your initialization,
however complex it is? The details depend on which subclass have which
slots of course. ---Madhu