[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

DEFMETHOD compile-time processing



   Date: Tue, 24 Jan 89 09:19:45 PST
   From: Patrick Dussud <dussud@lucid.com>

      Sender: GRAY@Kelvin.csc.ti.com
      Date: Mon, 23 Jan 89  11:15:30 CST
      From: David N Gray <Gray@DSG.csc.ti.com>

      In the Meta Object Protocol draft number 10 [89-003], on page 3-16 it says
      that 
	"At compile time: ... (5) The method function is computed by
	evaluating the special form (FUNCTION <lambda>) in the lexical
	environment of the DEFMETHOD.  ...  (7) The function ADD-METHOD is
	called ..."

      This isn't going to work.  You can install the function at load
      time in its lexical environment, or you can install it at compile time in
      the null lexical environment, but you can't evaluate something at
      compile-time in its run-time lexical environment.

   you're right. 

[A paraphase of your next paragraph:  At compile time, a "remote
environment" data structure must be set up to model the eventual state
of the "local environment" which results from loading the compiled code.
A design goal of CLOS is to make these two kinds of environments as similar
as possible, having the same programmatic interface if possible.  Yet, there
are objects which cannot be represented completely in a remote environment,
such as pieces of compiled code.]
  ...

      Possible remedies include:

       * Do the compile-time call to ADD-METHOD only if the DEFMETHOD appears at
	 top-level in a null lexical environment.  This would be consistent with
	 the treatment of DEFMACRO in proposal DEFINING-MACROS-NON-TOP-LEVEL.

   I don't consider this solution very satisfying, because it hides the problem.
   It is possible to represent functions for the remote environment by normal
   functions if the lexical environment is null, but still, they can't be
   executed. The problem remains. 

       * Don't ever do a compile-time call to ADD-METHOD.  I haven't seen a
	 reason why methods would need to be installed in the compile-time
	 environment.  Apparently at least some information about generic
	 function definitions needs to be remembered for use when invoking
	 MAKE-METHOD-LAMBDA, but that wouldn't require being able to actually
	 call the generic function at compile-time.

   This will lead to two different metaprogramming styles. Note that this is more
   or less what Flavors does. It is workable, but not pretty. Maybe a better
   solution is to standardize a representation for objects in the remote
   environment, and have the compile-file time expansion create them. Add-method
   and such work as before, function slots don't always contain real functions.

   Patrick.

In Lisp, remote function objects are usually represented by their names.
For example, if we want at compile time to get hold of a function FOO to
insert it into code or data structure (in the course of macroexpansion,
say), we naturally don't attempt to grab hold of the function object
itself, or any compile-time representation of it; we use the name FOO
itself as a reference to the function.  At compile time, the name may
not be defined, but we assume that it will be at load time.  Lisp's
dynamic linking will ensure that we will access the code we want.

I'd like to present some generalizations of this which apply to CLOS.

Interestingly, Lisp already unifies the treatment of remote functions
(i.e., their names) and local ones (compiled code objects), in the
following way: All operations which work on compiled code objects also
work on their names __if__ those names are bound in the local
environment.  For example, you can funcall or disassemble an FBOUNDP
symbol as easily as its binding, if it's bound.  A remote reference
is coerced to a local one, for local operations.

This is a principle of unity which can address the problem at hand.

The proposals for non-symbolic function names (some of which are
motivated by CLOS work) generalize the set of names you can give to
functions.  So, for example, if (SETF <name>) might be a valid function
name, so it can occur syntactically wherever a symbolic function name
might.

The semantics of non-symbolic function names (I contend) should tend to
preserve the unification between remote and local objects described
above.  For example, a list like (SETF <name>) should support the
programmatic operations common to symbols and code objects:
DISASSEMBLE, SYMBOL-FUNCTION, FBOUNDP and (yes) FUNCALL.

Did you hear me suggest allowing FBOUNDP and FUNCALL on lists?
You did.

Consider this: Common Lisp already allows FUNCALL on lists, in what
turns out to be another case of remote-to-local coercion.  If you
FUNCALL a list whose car is LAMBDA, the list is coerced to (or
interpreted as, which amounts to the same thing) the function it
specifies.  In other word, the cons tree which names a function remotely
can serve as that function's.  (By the way, an "anonymous" lambda is
really named by its cons tree.)  The reference is validated late, at the
point of call: Just as symbols are checked for fboundp-ness, lambda
forms are walked by the interpreter when called, and not earlier.
Nevertheless, the reference, once validated, is equivalent to the
object itself.

Given function specifiers for method code, and given the unification
supplied by remote-to-local coercion of all function specifiers,
we can then store remote function specifiers in method objects,
to refer to code, and both local and remote operations will work
on them.

The paragraph David Gray mentioned above would then look something
like this:

   At compile time: ... (5) The method function is set to the name of
   the method, e.g., (METHOD <gfspec> <quals> <specs>).  (This name may
   or may not be FBOUNDP at compile time.)  The form (DEFUN
   <method-name> <args> <body>) is compiled in the lexical environment
   of the DEFMETHOD.  ...  (7) The function ADD-METHOD is called ..."

   ... At load time, the DEFUN form for the method causes the method's
   name to become bound to the specified code.

At both compile and load time, the method's function would be the name
of the actual function, which would be reliably FBOUNDP at load time.
Doing something like (FUNCALL (METHOD-FUNCTION M) X Y Z) would work
whenever reasonable, and cause an unbound function error at other times.

Note: As I've said before, nonsymbolic names can be useful for referring
to other things than functions.  For example, mixture classes are
usefully named by cons trees of some sort.  Consider extending the type
constructor AND to classes: (AND MY-VIEWPORT-CLASS BORDERS-MIXIN
CANVAS-STREAM-MIXIN).  This could be implemented by extending
SYMBOL-CLASS to accept non-symbolic names, with a mixin manager which
builds and caches an otherwise-anonymous class for each mixture it sees.

Note also that a user-defined function specifier facility, with the
remote-to-local coercion rule, lets you build lambda-macros.
(Remember those?)

					-- John