[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: indefinite extent of call-next-method
I said I didn't have an opinion on this issue and I wasn't going to
say anything, but having once opened my mouth, I can't let this kind
of reasoning go by.
Date: Thu, 22 Oct 87 08:41:18 PDT
From: kempf%hplabsz@hplabs.HP.COM
> I don't find Jim's argument convincing at all. To my mind,
> call-next-method is a function that is closed over the identity
> of the next method and the arguments to be passed to it, just
> as a function defined by labels with a free reference that is
> captured in an outer scope is closed over the variable in the
> free reference. In both cases it's equally true and equally
> irrelevant to the scope of the closure that the value of the
> closed-over variable is only known at run time.
The difference is that the name of the free reference in the case of a function
defined by LABELS or FLET is lexically apparent, while the identity
of the next method and arguments to be passed for CALL-NEXT-METHOD are not.
Come on! The lexical bindings of CALL-NEXT-METHOD and NEXT-METHOD-P are
put in automatically by the DEFMETHOD macro. Suppose they looked like this:
(flet ((next-method-p ()
(not (null clos-internal:next-method-function)))
(call-next-method (&rest arguments)
(unless clos-internal:next-method-function
(error "There is no next method"))
(apply clos-internal:next-method-function
(or arguments clos-internal:next-method-arguments))))
...body of method...)
Yes, the identity of the next method and arguments are not lexically
apparent, just as the values of the freely-referenced variables in a
flet are not lexically apparent. I think you're confusing the names
with the values. The -only- way I see that call-next-method is
different from other lexically local functions is that its definition is
put in by a macro, thus is not lexically apparent in the original source
code. The reason for that of course is so we don't have to document the
names and contracts of the particular variables it looks at to decide
what to do, leaving implementations some freedom.
The only thing I can think is that you people arguing for dynamic extent
instead of indefinite extent have in the back of your minds that the
variables I have called clos-internal:next-method-function and
clos-internal:next-method-arguments in the example above should be
special variables rather than lexical variables, and they get dynamically
bound by generic function dispatch. But we already determined
long ago that that can't work; I don't want to spend the time to dredge
up the mail reference, but consider this contrived example:
(defmethod gf1 ((x class-1) f)
(funcall f))
(defmethod gf2 ((x class-2))
(gf1 (slot-value x 'foo) #'call-next-method))
The point here is that even with dynamic extent, call-next-method
can be called inside a generic-function call that is different from
the one whose next method it refers to. We want the next method for
gf2, not the next method for gf1. This next example is even better
since only one generic function is involved:
(defmethod gf3 ((x t) n f)
n)
(defmethod gf3 ((x class-1) n f)
(if (zerop n)
(funcall f)
(gf3 x (1- n) #'(lambda ()
(* (funcall f)
(call-next-method))))))
(gf3 (make-instance 'class-1) 6 #'(lambda () 1))
should return 720, but would return 0 if special variables
were used to control call-next-method.
This means that a programmer trying to understand program structure for a
function defined by labels or FLET need only look at the source where the
function is passed out of scope, while, with CALL-NEXT-METHOD, the programmer
must run the code.
It's true that to know which particular method CALL-NEXT-METHOD is going
to call, one has to know what the applicable methods are, which can vary
at run time. Isn't that more or less the whole point of the existence
of CALL-NEXT-METHOD? To me, this doesn't seem to have any bearing on
what the extent of CALL-NEXT-METHOD should be. I guess you see it
differently.
In the case of the function, the values are dynamically
determined but the control structure is not, modulo function valued variables.
In the case of CALL-NEXT-METHOD, the control structure is dynamically
determined.
I can't reconcile these two sentences. Is the control structure in
CALL-NEXT-METHOD anything other than function valued variables?
Anticipating the question of "how does this differ from generic
functions or a direct call to CALL-NEXT-METHOD", in both these cases there
are lexical cues that control structure is dynamically calculated. In the
case of a generic function invocation, it is the name of the generic
function, for which the application of SYMBOL-FUNCTION will return a
generic function object. In the case of CALL-NEXT-METHOD, it is the lexical
context of a method definition. An executable entity enclosing a
CALL-NEXT-METHOD passed out of scope could be any one of a number of
things, some of which will give no clue that they enclose a CALL-NEXT-METHOD.
I don't disagree with that, but can't see how it's relevant.
I understand your and Pavel's concern, however, I have a couple of problems
with this. I think it is important that the implications of making
CALL-NEXT-METHOD indefinite in extent be outlined up front, rather than
having implementors duplicate the discussion we've been going through.
This has been the case for other new concepts we've introduced, such as
generic functions. The other problem is one of design philosophy, which
is, of course, open to debate. I believe that it is usually a good idea
to offer people an "economy" option and a "luxury" option. Although
it's always dangerous to say what "most" people do, I think most
programmers won't be affected if CALL-NEXT-METHOD is implemented either
way.
I agree that passing a closure of CALL-NEXT-METHOD upward will be quite rare.
This is why I don't care much about this issue. I see it as only an academic
issue of language cleanliness, rather than something that will make a big
difference to users; the latter issues are the ones I care more about. Of
course I'd rather see a simpler and cleaner language too, if we can agree.
On the other hand, I can imagine certain "economy" CL implementations
in which implementing CALL-NEXT-METHOD as having indefinite extend may
be difficult, or adversely impact use of CALL-NEXT-METHOD in some manner.
Thus Dick's solution of specifying dynamic extent (as the "economy" option)
with "indefinite" as an allowable extention (as the "luxury" option)
appeals to me.
I still can't see how the issues for CALL-NEXT-METHOD are different from
the issues for any lexically local function, yet CLtL does not offer
implementations the choice of only implementing "downward" funargs.