From: Gabor Melis
Subject: make-instance :around
Date: 
Message-ID: <fb0fb805.0405211252.759f0d87@posting.google.com>
I'm trying to intercept make-instance calls for the user class but the
around method with the (eql 'user) specializer is not getting called
under some circumstances (see below). It is under CMUCL. Clisp does
OK.

(defclass user ()
  ())

(defmethod make-instance :around ((class (eql 'user)) &rest args)
  (declare (ignorable class args))
  (format t "AROUND/EQL-USER-SYMBOL~%")
  (force-output)
  (call-next-method))

(defmethod make-instance :around ((class (eql (find-class 'user)))
&rest args)
  (declare (ignorable class args))
  (format t "AROUND/EQL-USER-CLASS~%")
  (force-output)
  (call-next-method))

(defun make/symbol ()
  (make-instance 'user))

(defun make/class ()
  (make-instance (find-class 'user)))

(make/symbol)
;; when make/symbol is compiled it prints nothing
;; when make/symbol is not compiled it prints:
;; AROUND/EQL-USER-SYMBOL
(make/class)
;; prints:
;; AROUND/EQL-USER-SYMBOL

;; doing essentially the same with another method works
(defmethod gfun ((s (eql 'user)) &rest args)
  (declare (ignore s args))
  (format t "GFUN PRIMARY~%"))

(defmethod gfun :around ((s (eql 'user)) &rest args)
  (declare (ignore s args))
  (format t "GFUN AROUND/EQL-USER-SYMBOL~%")
  (force-output)
  (call-next-method))

(defun bar ()
  (gfun 'user))

(bar)
;; prints:
;; GFUN AROUND/EQL-USER-SYMBOL
;; GFUN PRIMARY

From: Steven M. Haflich
Subject: Re: make-instance :around
Date: 
Message-ID: <RnBrc.2323$tI4.1006@newssvr27.news.prodigy.com>
Gabor Melis wrote:
> I'm trying to intercept make-instance calls for the user class but the
> around method with the (eql 'user) specializer is not getting called
> under some circumstances (see below). It is under CMUCL. Clisp does
> OK.

See here, in the MOP specification, the first bullet:

http://www.franz.com/support/documentation/6.2/doc/mop/concepts.html#portable

Your around method isn't legal.  The MOP was designed to balance the tension
between customizablility and efficient execution.  Prohibitions like the one
cited allow the implementation to assume you won't mess around with the
various protocols for standard (i.e. "specified") metaclasses and this
is the only thing that allows efficient execution of CLOS with standard
metaclasses.
From: Gabor Melis
Subject: Re: make-instance :around
Date: 
Message-ID: <fb0fb805.0405220249.7357dfa4@posting.google.com>
"Steven M. Haflich" <·················@alum.mit.edu> wrote in message news:<···················@newssvr27.news.prodigy.com>...
> Gabor Melis wrote:
> > I'm trying to intercept make-instance calls for the user class but the
> > around method with the (eql 'user) specializer is not getting called
> > under some circumstances (see below). It is under CMUCL. Clisp does
> > OK.
> 
> See here, in the MOP specification, the first bullet:
> 
> http://www.franz.com/support/documentation/6.2/doc/mop/concepts.html#portable
> 
> Your around method isn't legal.  The MOP was designed to balance the tension
> between customizablility and efficient execution.  Prohibitions like the one
> cited allow the implementation to assume you won't mess around with the
> various protocols for standard (i.e. "specified") metaclasses and this
> is the only thing that allows efficient execution of CLOS with standard
> metaclasses.

Thanks. I take it the relevant quote is this:

"Portable methods on specified generic functions specialized to
portable metaobject classes must be defined before any instances of
those classes (or any subclasses) are created, either directly or
indirectly by a call to make-instance. Methods can be defined after
instances are created by allocate-instance however. Portable
metaobject classes cannot be redefined.

Implementation Note: The purpose of this last restriction is to permit
implementations to provide performance optimizations by analyzing, at
the time the first instance of a metaobject class is initialized, what
portable methods will be applicable to it. This can make it possible
to optimize calls to those specified generic functions which would
have no applicable portable methods."


However:

HyperSpec 7.1 Object Creation and Initialization:
"Each step of make-instance is implemented by a generic function to
provide a mechanism for customizing that step. In addition,
make-instance is itself a generic function and thus also can be
customized."

Is this just plain wrong?

Gabor
From: Bruno Haible
Subject: Re: make-instance :around
Date: 
Message-ID: <c97g25$b6f$1@laposte.ilog.fr>
Steven M. Haflich wrote:
>
> See here, in the MOP specification, the first bullet:
>
> http://www.franz.com/support/documentation/6.2/doc/mop/concepts.html#portable
>
> Your around method isn't legal.

Sorry, but the MOP specification you cite allows the shown MAKE-INSTANCE
methods. The shown methods "extend" the default behaviour (according
to the terminology in
http://www.franz.com/support/documentation/6.2/doc/mop/concepts.html#specialization-terminology)
and are therefore valid, portable user code:

  "Portable programs may define methods that extend specified methods
   unless the description of the specified method explicitly prohibits
   this. Unless there is a specific statement to the contrary, these
   extending methods must return whatever value was returned by the
   call to call-next-method."

Also, the ANSI CL section 7.1.7 explicitly allows the shown
MAKE-INSTANCE methods.

  "Customizing at the Programmer Interface level includes using the
   :initform, :initarg, and :default-initargs options to DEFCLASS, as
   well as defining methods for MAKE-INSTANCE, ..."

Some implementations have compiler optimizations like transforming

  (MAKE-INSTANCE symbol)

into

  (MAKE-INSTANCE (LOAD-TIME-VALUE (FIND-CLASS symbol)))

but these optimizations are apparently invalid because they break
valid user code. An optimization that is probably valid is to cache
the effective-method, thus transforming

  (MAKE-INSTANCE symbol)

into

  (FUNCALL (CAR (LOAD-TIME-VALUE
                  (let ((cache (list (compute-effective-method
                                       #'make-instance ...))))
                    (register-for-update-when-make-instance-changes
                      cache ...)
                    cache))))

Bruno