From: Pascal Costanza
Subject: More letf problems...
Date: 
Message-ID: <c8iami$lks$1@newsreader2.netcologne.de>
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/

From: Barry Margolin
Subject: Re: More letf problems...
Date: 
Message-ID: <barmar-EC6999.12002920052004@comcast.dca.giganews.com>
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 ***
From: Pascal Costanza
Subject: Re: More letf problems...
Date: 
Message-ID: <c8jang$mub$1@newsreader2.netcologne.de>
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/
From: Robert St. Amant
Subject: Re: More letf problems...
Date: 
Message-ID: <lpnn041c0si.fsf@haeckel.csc.ncsu.edu>
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
From: Pascal Costanza
Subject: Re: More letf problems...
Date: 
Message-ID: <c8l6kt$s2k$1@f1node01.rhrz.uni-bonn.de>
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/