From: Kenny Tilton
Subject: How can I hack 'make-instance?
Date: 
Message-ID: <37A7486F.94BBBCCD@liii.com>
I /think/ I have seen code do this, and it almost seems to work, but
having a very bizarre problem...


I want to code:

   (make-instance 'myClass :copying t)

...where :copying is not a slot or initarg of 'myClass. I want to detect
that in the 'initialize-instance to avoid some heavy lifting there:

(defmethod initialize-instance :after ((DX myClass) &rest iargs)
   (if (cadr (member :copying iargs))
     (trc "DX being copied")
     (progn 
	;heavy lifting
       )

What is so bizarre? This works on some classes but not others (and no
one has a "copying" initarg)...on some I get the error ":copying is not
a valid :initarg...."...no pattern detectible.

1. Well, is this legal anyway? If not, anyone know of a way to interfere
with 'i-i behavior on an ad hoc basis like this?

2. If it is legal (it works a little--I see "DX being copied" in the
trace!), geez, how /do/ i do this? Do I destructively modify the
initargs list in a before method on i-i?! yeeha.

cheers,

ken tilton
clinisys
acl 5.0.1
allegrostore 1.3
nt 4.0

From: David Bakhash
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <cxjyafs395n.fsf@acs5.bu.edu>
I'd just write your own make-instance.  Probably best to call it something
else.  if that doesn't suite your needs, then shadow the one in the CL package
and then redefine it in yours.

dave
From: Pierre R. Mai
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <87zp088un3.fsf@orion.dent.isdn.cs.tu-berlin.de>
David Bakhash <·····@bu.edu> writes:

> I'd just write your own make-instance.  Probably best to call it
> something else.  if that doesn't suite your needs, then shadow the
> one in the CL package and then redefine it in yours.

While writing your own, specialized make-instance function is often
preferable for a number of (unrelated) reasons (like the ability to
switch representations completely, by e.g. going to structures), in
this case, just declaring the initarg valid on initialize-instance is
sufficient.

And if that wasn't sufficient, you could always define a method on
make-instance (yes, make-instance is a generic funtion, too!),
eql-specialized on his class...

CLOS really gives you pretty good control of things...

Regs, Pierre.

-- 
Pierre Mai <····@acm.org>         PGP and GPG keys at your nearest Keyserver
  "One smaller motivation which, in part, stems from altruism is Microsoft-
   bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
From: Fernando Mato Mira
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <37A813F4.5CE95C21@iname.com>
"Pierre R. Mai" wrote:

> CLOS really gives you pretty good control of things...

How do you embed subobjects? (And copying writers?)
From: Howard R. Stearns
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <37A87697.7F97970B@elwood.com>
Fernando Mato Mira wrote:
> 
> "Pierre R. Mai" wrote:
> 
> > CLOS really gives you pretty good control of things...
> 
> How do you embed subobjects? (And copying writers?)

I don't know what you mean by embeded subobjects.  By copywing writer,
do you mean this:

(defclass my-class ()
  ((foo :accessor my-foo)))

(defmethod (setf my-foo) :around (value (container my-class))
  (call-next-method (frob-or-copy value) container))
From: Fernando Mato Mira
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <37A93E7D.22476869@iname.com>
"Howard R. Stearns" wrote:

> Fernando Mato Mira wrote:
> >
> > "Pierre R. Mai" wrote:
> >
> > > CLOS really gives you pretty good control of things...
> >
> > How do you embed subobjects? (And copying writers?)
>
> I don't know what you mean by embeded subobjects.  By copywing writer,
> do you mean this:
>
> (defclass my-class ()
>   ((foo :accessor my-foo)))
>
> (defmethod (setf my-foo) :around (value (container my-class))
>   (call-next-method (frob-or-copy value) container))

No. By `embedded' I mean it's not a reference, but that the subobject is
spliced
into the parent's memory block. This means that writers _must_ copy into
the destination (and that you'll normally want an additional dispatching
argument to specify which semantics you want (eg: (eql :shallow), (eql
:deep), (eql :whatever))

[Damm. But a dispatching argument cannot be &optional]
From: Pierre R. Mai
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <87n1w62scd.fsf@orion.dent.isdn.cs.tu-berlin.de>
Fernando Mato Mira <········@iname.com> writes:

> "Howard R. Stearns" wrote:
> 
> > Fernando Mato Mira wrote:
> > >
> > > "Pierre R. Mai" wrote:
> > >
> > > > CLOS really gives you pretty good control of things...
> > >
> > > How do you embed subobjects? (And copying writers?)
> >
> > I don't know what you mean by embeded subobjects.  By copywing writer,
> > do you mean this:
> >
> > (defclass my-class ()
> >   ((foo :accessor my-foo)))
> >
> > (defmethod (setf my-foo) :around (value (container my-class))
> >   (call-next-method (frob-or-copy value) container))
> 
> No. By `embedded' I mean it's not a reference, but that the subobject is
> spliced
> into the parent's memory block. This means that writers _must_ copy into
> the destination (and that you'll normally want an additional dispatching
> argument to specify which semantics you want (eg: (eql :shallow), (eql
> :deep), (eql :whatever))

This is generally not feasible, since there exists no generally useful 
definition of copying or equality on structured data.  Shallow and
Deep are just extremes on a very fluid scale.  So you would really
need to specify the copying function as part of the slot.  But this
would be problematic, too, since then this copying function would need 
to be aware of the internal storage arangement of the containing
class.  Not very good.

But if we ignore this, and don't try to make a general solution, you
could hack up something using the MOP (note that both writers and
readers would have to "copy", since the subobject(s) lose their
identity if stored as part of some anonymous storage block).

Another huge problem with this approach would be, that storage layout
would change every time you store an object of a different type in
this embedded slot.  And even if you restrict yourself to one type of
objects, things can change around, since you probably wouldn't want to 
prohibit the storage of sub-classed objects.  So this would be slow,
I'd guess.

Another _big_ problem with this approach is, that since both writers
and readers copy (i.e. recreate the object in question), simple
invariants like the following will break:

(let ((father (make-embedding-object))
      (anobject (make-an-object)))
  (setf (slot-value father 'embedded) anobject)
  (eql (slot-value father 'embedded) anobject))

This would suddenly return nil.  Not good.

So this get's us to the question: Why bother?  What would embedded
objects of this sort buy me, besides lots of hassles and philosophical
problems based on object identity?

Summary:  The MOP probably lets you do something like this, but the
rest of Lisp and logic will probably stand in your way.

Regs, Pierre.

PS: For the reasoning why equality and copying are not uniquely
defined for structured objects, see Kent M. Pitman's recent and not so 
recent postings here, and read his PS article on the topic, available
at http://world.std.com/~pitman/PS/EQUAL.html

-- 
Pierre Mai <····@acm.org>         PGP and GPG keys at your nearest Keyserver
  "One smaller motivation which, in part, stems from altruism is Microsoft-
   bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
From: Howard R. Stearns
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <37AB3E84.15C904E0@elwood.com>
Fernando Mato Mira wrote:
> 
> "Howard R. Stearns" wrote:
> 
> > Fernando Mato Mira wrote:
> > >
> > > "Pierre R. Mai" wrote:
> > >
> > > > CLOS really gives you pretty good control of things...
> > >
> > > How do you embed subobjects? (And copying writers?)
> >
> > I don't know what you mean by embeded subobjects.  By copywing writer,
> > do you mean this:
> >
> > (defclass my-class ()
> >   ((foo :accessor my-foo)))
> >
> > (defmethod (setf my-foo) :around (value (container my-class))
> >   (call-next-method (frob-or-copy value) container))
> 
> No. By `embedded' I mean it's not a reference, but that the subobject is
> spliced
> into the parent's memory block. This means that writers _must_ copy into
> the destination (and that you'll normally want an additional dispatching
> argument to specify which semantics you want (eg: (eql :shallow), (eql
> :deep), (eql :whatever))
> 
> [Damm. But a dispatching argument cannot be &optional]

Thanks for clarifying.  My understanding is that it isn't very clearly
understood how to manage embedding in any language.  From time to time I
run accross papers where someone has created yet another C/C++
preprocessor that implements some particular set of embedding/copying
semantics, but I haven't seen a clear and general understanding of the
issues emerge.  Am I missing something?

Given all the things we like about Lisp=>CLOS=>MOP, it does strike me as
a shame that more of these articles aren't written using an extended
Lisp as the example language.   It might be a useful exercise to define
those:
  1. extensions to the CLOS-MOP,
  2. hooks to the compiler, and
  3. declarations to the GC, 
that would be necesary in order to allow easy experimentation with
different metaclasses that defined different "native" storage
mechanisms.
From: David Bakhash
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <cxjwvvb37qi.fsf@acs5.bu.edu>
I am surprised.  I always thought that `make-instance' was a standard
function.  I wonder how much is gained from making it generic.  It can only
dispatch on one arg, which is always a symbol.  If you use eql to give
make-instance multiple methods, say 4 times, then what?  every non-overloaded
make-instance call have to fall through a case statement to the fifth case?

If you're gonna add that kind of overhead, then I believe that it's better
practice to make new constructors, such as

(defclass classroom ...) ==> (defun make-classroom).

I think that was also one of the suggestions in the Keene book.

dave
From: Robert Monfera
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <37A85DB3.9D365186@fisec.com>
David Bakhash wrote:

> I am surprised.  I always thought that `make-instance' was a standard
> function.  I wonder how much is gained from making it generic.  It can only
> dispatch on one arg, which is always a symbol.  

Make-instance may also take a class as its argument, which is in one
sense more fundamental than its ability to take a symbol in that the
symbol will eventually map to a class.

The first argument is actually being used for dispatching with the
following specializations: (standard-class) (symbol)

You may want to use it if your metaclasses have special requirements,
although some other choices may sometimes be better (e.g.,
allocate-instance).  You may also hang some :before and :after methods
onto it.

Robert
From: Pierre R. Mai
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <87so5z5ttz.fsf@orion.dent.isdn.cs.tu-berlin.de>
David Bakhash <·····@bu.edu> writes:

> I am surprised.  I always thought that `make-instance' was a standard
> function.  I wonder how much is gained from making it generic.  It can only
> dispatch on one arg, which is always a symbol.  If you use eql to give
> make-instance multiple methods, say 4 times, then what?  every non-overloaded
> make-instance call have to fall through a case statement to the fifth case?

As Robert has already pointed out, the first argument can also be a
class object, and it doesn't really use eql-specializing, but looks up 
the class object if given a symbol and dispatches on that.

But regardless of this, eql-dispatching isn't that slow really, and isn't
really implemented via case statements.  Most CLOS implementations use
some form of caching to speed up method dispatching, and EQL-specializiers
aren't much slower using that than normal specializers.  Here is a
short snip of results for versions of CMU CL and ACL I benchmarked
some time ago.  Note that this should again not be taken as any
indication of actual performance, it's just to give you an idea of
relative eql-specializer performance (benchmarks were prepared some
time ago, and the machine in question had ~ P90 integer and general
performance):

Speed of Allegro CL Trial Edition 5.0 [Linux/X86] (8/29/98 10:57)
on Linux/X86 dent.
ACL 5.0 Linux

Call a function with no arguments that just returns NIL:
Takes 0.46 microseconds (average of 1,000,000 iterations).

[...]

Call a default method (the only method for the generic) with one argument:
Takes 0.49 microseconds (average of 1,000,000 iterations).

Call a generic function with one method with one argument, a standard-object:
Takes 1.33 microseconds (average of 1,000,000 iterations).

Call a generic function with three methods with one argument, a standard-object:
Takes 1.21 microseconds (average of 1,000,000 iterations).

Call a generic function with three methods with one argument, a fixnum:
Takes 1.82 microseconds (average of 1,000,000 iterations).

Call a reader method (the only method for the generic) with one argument:
Takes 1.01 microseconds (average of 1,000,000 iterations).

Call a writer method (the only method for the generic) with two arguments:
Takes 1.31 microseconds (average of 1,000,000 iterations).

Call one EQL-specialized method out of 1:
Takes 5.14 microseconds (average of 100,000 iterations).

Call default method after missing one EQL-specialized method out of 1:
Takes 6.10 microseconds (average of 100,000 iterations).

Call one EQL-specialized method out of 10:
Takes 9.30 microseconds (average of 100,000 iterations).

Call default method after missing one EQL-specialized method out of 10:
Takes 13.00 microseconds (average of 100,000 iterations).

Call one EQL-specialized method out of 100:
Takes 42.40 microseconds (average of 10,000 iterations).

Call default method after missing one EQL-specialized method out of 100:
Takes 80.00 microseconds (average of 10,000 iterations).

Call one EQL-specialized multimethod out of 10:
Takes 10.50 microseconds (average of 100,000 iterations).

Call default method after missing one EQL-specialized multimethod out of 10:
Takes 14.60 microseconds (average of 100,000 iterations).

[... now for make-instance performance ...]

MAKE-INSTANCE of a constant class with two slots and no initialization methods:
Takes 5.28 microseconds (average of 100,000 iterations).

MAKE-INSTANCE of a constant class with four slots and no initialization methods:
Takes 5.78 microseconds (average of 100,000 iterations).

MAKE-INSTANCE of a constant class with four slots and one initialization method:
Takes 9.00 microseconds (average of 100,000 iterations).

MAKE-INSTANCE of a variable class by name with two slots and no initialization methods:
Takes 124.60 microseconds (average of 10,000 iterations).

MAKE-INSTANCE of a variable class by object with two slots and no initialization methods:
Takes 101.40 microseconds (average of 10,000 iterations).

And CMU CL:

Speed of CMU Common Lisp 18a+ release x86-linux 2.4.5 29 June 1998 cvs
on X86 dent.
CMU CL 2.4.5

Call a function with no arguments that just returns NIL:
Takes 0.58 microseconds (average of 10,000,000 iterations).

[...]

Call a default method (the only method for the generic) with one argument:
Takes 1.09 microseconds (average of 1,000,000 iterations).

Call a generic function with one method with one argument, a standard-object:
Takes 1.29 microseconds (average of 1,000,000 iterations).

Call a generic function with three methods with one argument, a standard-object:
Takes 1.28 microseconds (average of 1,000,000 iterations).

Call a generic function with three methods with one argument, a fixnum:
Takes 1.59 microseconds (average of 1,000,000 iterations).

Call a reader method (the only method for the generic) with one argument:
Takes 0.93 microseconds (average of 10,000,000 iterations).

Call a writer method (the only method for the generic) with two arguments:
Takes 0.95 microseconds (average of 10,000,000 iterations).

Call one EQL-specialized method out of 1:
Takes 1.12 microseconds (average of 1,000,000 iterations).

Call default method after missing one EQL-specialized method out of 1:
Takes 1.13 microseconds (average of 1,000,000 iterations).

Call one EQL-specialized method out of 10:
Takes 1.70 microseconds (average of 1,000,000 iterations).

Call default method after missing one EQL-specialized method out of 10:
Takes 2.37 microseconds (average of 1,000,000 iterations).

Call one EQL-specialized method out of 100:
Takes 3.92 microseconds (average of 1,000,000 iterations).

Call default method after missing one EQL-specialized method out of 100:
Takes 4.05 microseconds (average of 1,000,000 iterations).

Call one EQL-specialized multimethod out of 10:
Takes 3.60 microseconds (average of 1,000,000 iterations).

Call default method after missing one EQL-specialized multimethod out of 10:
Takes 4.15 microseconds (average of 1,000,000 iterations).

[... now for make-instance performance ...]

MAKE-INSTANCE of a constant class with two slots and no initialization methods:
Takes 33.48 microseconds (average of 100,000 iterations).

MAKE-INSTANCE of a constant class with four slots and no initialization methods:
Takes 45.38 microseconds (average of 100,000 iterations).

MAKE-INSTANCE of a constant class with four slots and one initialization method:
Takes 49.38 microseconds (average of 100,000 iterations).

MAKE-INSTANCE of a variable class by name with two slots and no initialization methods:
Takes 126.80 microseconds (average of 10,000 iterations).

MAKE-INSTANCE of a variable class by object with two slots and no initialization methods:
Takes 105.60 microseconds (average of 10,000 iterations).

> If you're gonna add that kind of overhead, then I believe that it's better
> practice to make new constructors, such as
> 
> (defclass classroom ...) ==> (defun make-classroom).
> 
> I think that was also one of the suggestions in the Keene book.

As can be seen above, the fact that make-instance is often not very
fast is not related to the use of eql-specialization, or the fact
that make-instance is a generic function.  Modern implementations try
to optimize calls to make-instance, where they can know up-front, what 
class is passed.  OTOH, since this case is quite common anyway, I
quite like PCL's defconstructor macro, which allows you to define
class-specific constructors.  This not only cuts out gf and dispatch
overhead, but also keyword-parsing overhead, many validity checks,
etc.  OTOH defconstructor needs access to the class definition at
compile-time.  See the results of a simple benchmark:

Class:

(defclass my-class () 
  ((a :initarg :a :accessor my-class-a)
   (b :initarg :b :initform 0 :accessor my-class-b)))

Constructors:

(pcl::defconstructor make-my-class my-class (a b)
  :a a :b b)

(pcl::defconstructor make-my-class2 my-class (a &optional b)
  :a a :b (or b 0))

(pcl::defconstructor make-my-class3 my-class (a &key b)
  :a a :b (or b 0))

Results of time to generate 250000 instances:

Method					real(ms)  gc(ms)  consed bytes
----------------------------------------------------------------------
(make-instance 'my-class :a 5 :b 3)	    2930     430      17934824
(make-my-class 5 3)			     840     220       7970792
(make-my-class2 5 3)			     800     210       7967608
(make-my-class2 5)			     790     230       7970792
(make-my-class3 5 :b 3)			     840     220       7968320
(make-my-class3 5)			     810     200       7970728

So the time to create one instance goes down from 11.7µs to around
3.2µs.  And that's on an old-fashioned AMD-K6-2 350.  I'd say that
unless you are generating huge amounts of instances very quickly
indeed, make-instance performance is not really a problem.

So Keene's advice of providing your own specialized constructors
should be heeded not because of speed advantages, but to increase
your flexibility.  If you define your own constructor function, you
are then free to let that be a wrapper to make-instance, or to a fast
constructor (like those provided by PCL's defconstructor), or to a
defstruct-constructor, should you change the representation for some
reason, etc.

Regs, Pierre.

-- 
Pierre Mai <····@acm.org>         PGP and GPG keys at your nearest Keyserver
  "One smaller motivation which, in part, stems from altruism is Microsoft-
   bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
From: Tim Bradshaw
Subject: Re: How can I hack 'make-instance?
Date: 
Message-ID: <ey3btcnlf88.fsf@lostwithiel.tfeb.org>
* David Bakhash wrote:
> I am surprised.  I always thought that `make-instance' was a standard
> function.  I wonder how much is gained from making it generic.  It can only
> dispatch on one arg, which is always a symbol.  

No, it's not always a symbol!  In fact a likely definition of one
method on MAKE-INSTANCE is something like:

	(defmethod make-instance ((c symbol) &rest initargs)
	  (apply #'make-instance (find-class c) initargs))

The more general form of MAKE-INSTANCE can be usefully specialised if
you create instances of classes whose metaclass is not STANDARD-CLASS.

--tim