Hi,
I have been making good progress in implementing a semantically sound LETF.
Here are some of the intermediate results:
- I have defined a metaclass that allows classes to declare "special"
fields - fields that can be rebound with dynamic extent. This means that
things like the following work without affecting other threads:
(dletf (((name person) "Mr. Hide")))
...)
- As you see, I have chosen to call this operator DLETF instead of LETF.
I am convinced that LETF makes expect you wrong things: The default
binding behavior in Common Lisp is lexical, not dynamic, so it is more
reasonable to expect LETF to introduce lexical bindings. I have played
around with making LETF automatically decide whether it deals with a
lexical or a dynamic binding, like LET does for lexical and special
variables, but that turned out far too complicated. [1]
- Furthermore, implementing a lexical LETF sucks heavily. Since I don't
have good ideas what you can use it for, I have abandoned that for the
time being. [2] (So in effect, there will only be a DLETF on that level.)
- The next thing I have worked on is WITH-DYNAMICALLY-ADDED-METHODS
(again, I am using a very explicit name in order to avoid possible
confusions like those that have apparently occurred wrt
WITH-ADDED-METHODS during CLOS standardization).
There is one bit wrt this operator that is not really clear to me. Let's
first recap what the reasonable semantics for DLETF on AREF are. Given
the following expression:
(let ((array #(0 0 0 0 0)))
(dletf (((aref array 2) 5)))
(setf (aref array 2) 10)
(setf (aref array 3) 10))
array)
Here, one would expect that the result of the expression would be #(0 0
0 10 0) - only the element at index 2 is dynamically rebound, so all
SETFs to other elements should "write through".
Now, I am thinking about what one would want in the following scenario.
(defmetod print-person ((person person)) ; (1)
(print (name person)))
(with-dynamically-added-methods
((print-person :before ((person person)) ; (2)
(print "I am about to print a person name.")))
(defmethod print-person ((person person)) ; (3)
(print (name person))
(print (address person)))
(defmethod print-person :before ((person person)) ; (4)
(print "I am about to print a person's name and address."))
...)
(print person)
Here, method (3) replaces method (1), and method (4) replaces method (2)
in the dynamic extent of the WITH-DYNAMICALLY-ADDED-METHODS form. [3]
Now, the :before method was dynamically rebound, so afterwards method
(4) should be implicitly removed, so that no :before method is active
anymore in this example. So far, so good.
Question: Analogous to the DLETF on AREF example, method (3) should
"write through", so that it is still in effect after the control flow
has moved out of the WITH-DYNAMICALLY-ADDED-METHODS form, right? Or
would you expect that method (3) is also removed afterwards, and method
(1) will be reused again? In that case, the name
WITH-DYNAMICALLY-ADDED-METHODS would be misleading, but it should rather
be called GENERIC-DFLET, or some such.
I appreciate any hints and opinions that help me to resolve this issue.
Thanks,
Pascal
[1] I think it would have been better if Common Lisp had gone the route
ISLISP has taken, i.e. to introduce different binding constructs for
dynamic and lexical variables. Indeed, it is possible to implement your
own LET vs. DLET in Common Lisp, with appropriate warnings/errors if you
are not using them correctly, and I have tried that out. I think it
improves your code, but on the other hand I don't want to deviate too
far from CL in that regard. (You need accessors for environment objects
in order to make this work well.)
[2] The only "useful" usage scenario I can think of is to be able to
implement WITH-ADDED-METHODS correctly. But then again, it seems to me
by now that the purpose of a lexical WITH-ADDED-METHOD would only have
been to provide some kind of module mechanism. Since I think that
modules suck and CL-style packages are far superior, I don't think that
I want to go there.
[3] See the entry for DEFMETHOD in the HyperSpec: "If the generic
function already has a method that agrees with the method being defined
on parameter specializers and qualifiers, defmethod replaces the
existing method with the one now being defined."
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
In article <············@newsreader2.netcologne.de>,
Pascal Costanza <········@web.de> wrote:
> Now, I am thinking about what one would want in the following scenario.
>
> (defmetod print-person ((person person)) ; (1)
> (print (name person)))
>
> (with-dynamically-added-methods
>
> ((print-person :before ((person person)) ; (2)
> (print "I am about to print a person name.")))
>
> (defmethod print-person ((person person)) ; (3)
> (print (name person))
> (print (address person)))
>
> (defmethod print-person :before ((person person)) ; (4)
> (print "I am about to print a person's name and address."))
>
> ...)
>
> (print person)
>
> Here, method (3) replaces method (1), and method (4) replaces method (2)
> in the dynamic extent of the WITH-DYNAMICALLY-ADDED-METHODS form. [3]
> Now, the :before method was dynamically rebound, so afterwards method
> (4) should be implicitly removed, so that no :before method is active
> anymore in this example. So far, so good.
>
> Question: Analogous to the DLETF on AREF example, method (3) should
> "write through", so that it is still in effect after the control flow
> has moved out of the WITH-DYNAMICALLY-ADDED-METHODS form, right? Or
> would you expect that method (3) is also removed afterwards, and method
> (1) will be reused again? In that case, the name
> WITH-DYNAMICALLY-ADDED-METHODS would be misleading, but it should rather
> be called GENERIC-DFLET, or some such.
I would expect that the new primary method should persist, while the new
:BEFORE method should be removed.
However, I wouldn't expect anyone to complain too loudly if you
specified something like "the consequences are undefined if any changes
are made to the generic functions within the body of W-D-A-M." This is
kind of like the limitations on modifying a sequence within a traversal
function -- the bookkeeping necessary to implement well-defined and
useful semantics can be too burdensome, and the benefit isn't worth it.
--
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
Barry Margolin wrote:
> I would expect that the new primary method should persist, while the new
> :BEFORE method should be removed.
OK, that's what I also think.
> However, I wouldn't expect anyone to complain too loudly if you
> specified something like "the consequences are undefined if any changes
> are made to the generic functions within the body of W-D-A-M." This is
> kind of like the limitations on modifying a sequence within a traversal
> function -- the bookkeeping necessary to implement well-defined and
> useful semantics can be too burdensome, and the benefit isn't worth it.
I have already implemented the variant in which the primary method
wouldn't persist, and I am relatively sure that I know how to implement
the other one. So it's just a matter of making the right decision. But
maybe you're right when it comes to not affecting portability between
different CL implementations too much. I'll have to experiment some more
in this regard.
Anyway, thanks a lot for you feedback.
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Pascal Costanza <········@web.de> writes:
> Now, I am thinking about what one would want in the following scenario.
>
> (defmetod print-person ((person person)) ; (1)
> (print (name person)))
>
> (with-dynamically-added-methods
>
> ((print-person :before ((person person)) ; (2)
> (print "I am about to print a person name.")))
>
> (defmethod print-person ((person person)) ; (3)
> (print (name person))
> (print (address person)))
>
> (defmethod print-person :before ((person person)) ; (4)
> (print "I am about to print a person's name and address."))
>
> ...)
>
> (print person)
>
> Here, method (3) replaces method (1), and method (4) replaces method
> (2) in the dynamic extent of the WITH-DYNAMICALLY-ADDED-METHODS
> form. [3] Now, the :before method was dynamically rebound, so
> afterwards method (4) should be implicitly removed, so that no :before
> method is active anymore in this example. So far, so good.
Not to answer your question, but suppose I write,
(defmethod do-something ((n number))
(print n))
(with-dynamically-added-methods
((do-something ((n float))
(print (ceiling n))))
(defmethod do-something :before ((n float))
(print "Printing ceiling"))
...)
I don't have much experience in language design, so this question may
be naive, but if this is supported, would the :before method go away
outside the scope of W-D-A-M?
--
Rob St. Amant
http://www4.ncsu.edu/~stamant
Robert St. Amant wrote:
> Pascal Costanza <········@web.de> writes:
>
>
>>Now, I am thinking about what one would want in the following scenario.
>>
>>(defmetod print-person ((person person)) ; (1)
>> (print (name person)))
>>
>>(with-dynamically-added-methods
>>
>> ((print-person :before ((person person)) ; (2)
>> (print "I am about to print a person name.")))
>>
>> (defmethod print-person ((person person)) ; (3)
>> (print (name person))
>> (print (address person)))
>>
>> (defmethod print-person :before ((person person)) ; (4)
>> (print "I am about to print a person's name and address."))
>>
>> ...)
>>
>>(print person)
>>
>>Here, method (3) replaces method (1), and method (4) replaces method
>>(2) in the dynamic extent of the WITH-DYNAMICALLY-ADDED-METHODS
>>form. [3] Now, the :before method was dynamically rebound, so
>>afterwards method (4) should be implicitly removed, so that no :before
>>method is active anymore in this example. So far, so good.
>
> Not to answer your question, but suppose I write,
>
> (defmethod do-something ((n number))
> (print n))
>
> (with-dynamically-added-methods
> ((do-something ((n float))
> (print (ceiling n))))
>
> (defmethod do-something :before ((n float))
> (print "Printing ceiling"))
>
> ...)
>
> I don't have much experience in language design, so this question may
> be naive, but if this is supported, would the :before method go away
> outside the scope of W-D-A-M?
I don't think this is naive. No, the :before method wouldn't go away.
This isn't as dramatic as it looks at first - you can already (without
W-D-A-M) have just :before methods defined for specific specializers.
It's just not a good idea to call them. ;)
Anyway, I think what's confusing here is that the two ways to define
methods look too similar. I am currently thinking about a different
design, like this:
(with-dynamic-generic-function-scope (print-person do-something)
(defmethod print-person :dynamic :before ((person person))
(print "I am about to print a person."))
(defmethod do-something :before ((n float))
(print "Printing ceiling."))
...)
The idea is that :dynamic indicates "in the current dynamic environment
of the respective generic function", while the omission of :dynamic
means "replace a possibly existing method definition, or else define it
globally". (One could also add a :global option here to indicate "define
it globally in any case", but I don't think that would be useful.)
An interesting feature, IMHO, would be that you could refer to the
definition of a method in the previous dynamic environment like this:
(defmethod print-person ((person person))
(print (name person)))
(with-dynamic-generic-function-scope (print-person)
(defmethod print-person :dynamic ((person person))
(call-next-method) ; do whatever is usually done
; for that specializer
(print (age person))) ; in this dynamic environment,
; we also want to see a person's age
...)
I think this can be a useful generalization of my previous dflet [1]
approach.
Pascal
[1] See http://www.pascalcostanza.de/dynfun.pdf
--
ECOOP 2004 Workshops - Oslo, Norway
*1st European Lisp and Scheme Workshop, June 13*
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
*2nd Post-Java Workshop, June 14*
http://prog.vub.ac.be/~wdmeuter/PostJava04/