From: Michal Krupka
Subject: Nonstandard method combinations?
Date: 
Message-ID: <ehvoe4$i75$1@chiara.aioe.org>
I am curious where it is useful to use nonstandard method combinations 
in place of the standard one. I can see little (if any at all) uses of 
them in practice and am wondering why. Is this a proof that there is 
little or no practical use for them? Why were nonstandard method 
combination invented? As far as I can understand, many parts of the 
ANSI CL standard were created because they are really needed. However, 
I am not sure whether this can be said about all parts of CLOS.

My understanding is that since the standard method combination is so 
general, nonstandard method combinations could be used mostly in 
situations where authors of generic functions want to restrict in some 
ways the means by which methods can be overwritten. Such method 
combinations can be (and probably are) easily emulated by the standard 
one. So is this the reason? That we don't like restrictions?

There is only one nonstandard method combination used in the practice I 
am aware of which is not easily emulated by the standard one. I'll show 
it in the point 3. of this post.

It would be interesting to see some examples of practical use (!) of 
nonstandard method combinations. I can contribute the following:

1. progn method combination for initialization and finalization. I've 
seen it in several places (cells, some of Dimitry Ivanov's sources 
etc.).

(defgeneric initialize-something (something)
  (:method-combination progn :most-specific-last))

(defgeneric finalize-something (something)
  (:method-combination progn))

A variant of this is used in PCL, 
http://www.gigamonkeys.com/book/practical-parsing-binary-files.html for 
reading and writing object from/to a stream.

2. An interesting example of the and method combination was mentioned 
in this group several weeks ago:

(defgeneric equal-objects (obj1 obj2)
  (:method-combination and))

3. I used a custom method combination in a commercial project several 
years ago. It is interesting that this method combination is not used 
together with inheritance but rather as a run-time function editing 
tool. In my application, I had a simple patch system allowing 
application upgrades by loading small fasl-based patches. After 
upgrading, it was sometimes necessary to upgrade a config file to a 
newer version. I had two integer variables: *config-file-version*, and 
*program-version*, where the latter was incremented by patches. Thus, 
when the value of *program-version* was greater than 
*config-file-version*, the config file needed one or more upgrades 
(several patches could be loaded together).  In such a case, function 
upgrade-config-file with the value of *config-file-version* was called. 
The function was generic, built from methods supplied by particular 
patches. Integer qualifiers of the methods indicated new versions 
provided by the patches minus 1. The function upgrade-config-file 
executed only methods with qualifiers equal to or greater than the 
supplied argument.

(define-method-combination sort-and-cut ()
  ((methods positive-integer-qualifier-p))
  (:arguments from)
  (flet ((met-stage (met)
            (first (method-qualifiers met))))
    `(progn ,@(mapcar #'(lambda (method)
                          `(when (>= ,(met-stage method) ,from)
                             (call-method ,method)))
                      (stable-sort methods #'< :key #'met-stage))
       nil)))
    
(defun positive-integer-qualifier-p (method-qualifiers)
  (and (= (length method-qualifiers) 1)
       (typep (first method-qualifiers) '(integer 0 *))))


Example:

(defgeneric upgrade-config-file (stage)
  (:method-combination sort-and-cut))

(defmethod upgrade-config-file 0 (stage)
  (print "Upgraded from version 0 to 1"))

(defmethod upgrade-config-file 1 (stage)
  (print "Upgraded from version 1 to 2"))

(defmethod upgrade-config-file 2 (stage)
  (print "Upgraded from version 2 to 3"))

(defmethod upgrade-config-file 3 (stage)
  (print "Upgraded from version 3 to 4")) 


Now, executing

(upgrade-config-file 2)

prints

"Upgraded from version 2 to 3" 
"Upgraded from version 3 to 4"

The advantage of this solution was that patches don't need to redefine 
the function upgrade-config-file in a whole, but only to change it so 
it knows how to upgrade the config file from the last to the latest 
version.

Unfortunately, this trick doesn't work in all implementations (my was 
LispWorks).

Any other interesting examples?

Michal

From: Barry Margolin
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <barmar-336103.10550428102006@comcast.dca.giganews.com>
In article <············@chiara.aioe.org>,
 Michal Krupka <·······@mac.com> wrote:

> I am curious where it is useful to use nonstandard method combinations 
> in place of the standard one. I can see little (if any at all) uses of 
> them in practice and am wondering why. Is this a proof that there is 
> little or no practical use for them? Why were nonstandard method 
> combination invented? As far as I can understand, many parts of the 
> ANSI CL standard were created because they are really needed. However, 
> I am not sure whether this can be said about all parts of CLOS.

To allow for experimentation and growth, because we didn't want to 
presume that we'd thought of all the useful cases.  Lisp has almost 
always been designed with lots of flexibility and generality, and the 
language as you see it now is due to adopting as standard many things 
that were once user-written.  Even some things we now consider basic, 
such as LET and DEFMACRO, were not built into earlier dialects like 
MACLISP.

> 1. progn method combination for initialization and finalization. I've 
> seen it in several places (cells, some of Dimitry Ivanov's sources 
> etc.).

Ahh, when I wrote the above, I thought you were talking about the 
ability to create NEW method combinations.

While PROGN method combination *can* be emulated using :BEFORE or :AFTER 
methods, this requires all the method writers to remember that this is 
being done.  Method combinations like this provide self-documentation, 
as well as automating the simple tasks.  Common Lisp is not intended to 
be a "minimal" language -- we don't leave out useful features just 
because they can be built on top of more basic features (that's more 
like Scheme's philosophy); Common Lisp is a "kitchen sink" language.

After all, isn't this why we have method combinations in the first 
place?  Most other OO languages do without them at all, depending on the 
programmers to call the superclass methods directly.  We use all these 
method

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Pascal Costanza
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <4qh8ddFnb4dmU1@individual.net>
Michal Krupka wrote:

> Any other interesting examples?

- There is a method combination for special functions in AspectL. 
However, AspectL is an experimental language. I don't think the method 
combination has ever been used in "real" situations.

- User-defined method combinations can be used to implement Beta-style 
"inner" method calls. See 
http://groups.google.com/group/comp.lang.lisp/msg/60353ea473b7493e

- Method combinations were not new in CLOS. The direct precursors were 
method combinations (or "demons") in Flavors and LOOPS. They also go 
back to advices in Interlisp and BBN Lisp (a variant of which you can 
still find at least in LispWorks). They were "invented" in Warren 
Teitelman's PhD thesis in the 1960's (and were probably influenced by 
Oliver Selfridge's "Pandemonium" paper from the 1950's).

You can probably find a few examples throughout the history of these 
approaches. A standard case is notification on slot changes.

- In one case, a user-defined method combination helped me to get out of 
a corner I have painted myself into. It allowed me to turn a number of 
generic functions into function generators, so calling these generic 
functions didn't actually execute them, but rather produced (two 
versions) of the effective method function which I could then select at 
the call site depending on dynamic conditions.

- In general, I'd say that user-defined method combinations are one of 
those features that you probably only need in 5% of the time, but _when_ 
you need them, they are damn useful. ;)

- I consider it a design mistake that the standard method combination is 
the default. ISLISP provides another predefined 'nil method combination 
that only accepts primary methods, and I think this would have been a 
better default. Method combinations open up the possibilities what kinds 
of methods can be added to a generic function, and that decision should 
better be made explicit. The standard method combination is already too 
open for a default extension mechanism.

- For that matter, I consider it a design mistake that you can omit a 
defgeneric form and that defmethod implicitly creates a generic function 
when there is no defgeneric form. But that's a different (but related) 
story.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Barry Margolin
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <barmar-12B4D2.11093428102006@comcast.dca.giganews.com>
In article <··············@individual.net>,
 Pascal Costanza <··@p-cos.net> wrote:

> - I consider it a design mistake that the standard method combination is 
> the default. ISLISP provides another predefined 'nil method combination 
> that only accepts primary methods, and I think this would have been a 
> better default. Method combinations open up the possibilities what kinds 
> of methods can be added to a generic function, and that decision should 
> better be made explicit. The standard method combination is already too 
> open for a default extension mechanism.

CLOS's design was primarily motivated by Flavors, and I think the 
experience of Flavors users was that before- and after-methods were used 
very frequently.  They were used so much that this was considered the 
most natural way, and it was made the default.

Another reason is that the original generic function designer may not 
anticipate the need for subclasses to extend, rather than override, 
primary methods.  Standard method combination allows for these 
unanticipated extensions.  While it's true that this could still be done 
using around-methods or primary methods that call CALL-NEXT-METHOD 
explicitly, these are not considered as natural as before- and 
after-methods.  It's also counter to the Lisp philosophy of not 
prohibiting useful programming constructs -- this is the anti-B&D 
language.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Pascal Costanza
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <4qhgitFn9pgiU1@individual.net>
Barry Margolin wrote:
> In article <··············@individual.net>,
>  Pascal Costanza <··@p-cos.net> wrote:
> 
>> - I consider it a design mistake that the standard method combination is 
>> the default. ISLISP provides another predefined 'nil method combination 
>> that only accepts primary methods, and I think this would have been a 
>> better default. Method combinations open up the possibilities what kinds 
>> of methods can be added to a generic function, and that decision should 
>> better be made explicit. The standard method combination is already too 
>> open for a default extension mechanism.
> 
> CLOS's design was primarily motivated by Flavors, and I think the 
> experience of Flavors users was that before- and after-methods were used 
> very frequently.  They were used so much that this was considered the 
> most natural way, and it was made the default.

OK, I wasn't aware of this. (I only recall reading some rationale for 
dropping method combinations from Dylan - and indeed, I think dropping 
them completely would definitely go too far.)


Thanks,
Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Michal Krupka
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <ei01ta$4dm$1@chiara.aioe.org>
BTW, I've tried the code on LispWorks, SBCL, and Allegro, and only 
LispWorks does not lead to error after executing (upgrade-config-file 
2). The error is "More than one method of type METHODS with the same 
specializers." Is there an error in my code?

Michal

On 2006-10-28 16:11:14 +0200, Michal Krupka <·······@mac.com> said:

(define-method-combination sort-and-cut ()
  ((methods positive-integer-qualifier-p))
  (:arguments from)
  (flet ((met-stage (met)
            (first (method-qualifiers met))))
    `(progn ,@(mapcar #'(lambda (method)
                          `(when (>= ,(met-stage method) ,from)
                             (call-method ,method)))
                      (stable-sort methods #'< :key #'met-stage))
       nil)))
    (defun positive-integer-qualifier-p (method-qualifiers)
  (and (= (length method-qualifiers) 1)
       (typep (first method-qualifiers) '(integer 0 *))))

(defgeneric upgrade-config-file (stage)
  (:method-combination sort-and-cut))

(defmethod upgrade-config-file 0 (stage)
  (print "Upgraded from version 0 to 1"))

(defmethod upgrade-config-file 1 (stage)
  (print "Upgraded from version 1 to 2"))

(defmethod upgrade-config-file 2 (stage)
  (print "Upgraded from version 2 to 3"))

(defmethod upgrade-config-file 3 (stage)
  (print "Upgraded from version 3 to 4"))

(upgrade-config-file 2)
From: Christophe Rhodes
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <sqmz7grnh8.fsf@cantab.net>
Michal Krupka <·······@mac.com> writes:

> BTW, I've tried the code on LispWorks, SBCL, and Allegro, and only
> LispWorks does not lead to error after executing (upgrade-config-file
> 2). The error is "More than one method of type METHODS with the same
> specializers." Is there an error in my code?

Formally, maybe.  Buried in the spec for the long form of
DEFINE-METHOD-COMBINATION, there is the lovely wording (and I
apologize for the long and complex quote, but this is what we get to
play with):

  Note that two methods with identical specializers, but with
  different qualifiers, are not ordered by the algorithm described in
  Step 2 of the method selection and combination process described in
  Section 7.6.6 (Method Selection and Combination). Normally the two
  methods play different roles in the effective method because they
  have different qualifiers, and no matter how they are ordered in the
  result of Step 2, the effective method is the same. If the two
  methods play the same role and their order matters, an error is
  signaled. This happens as part of the qualifier pattern matching in
  define-method-combination.

So, you have several methods with the same specializers playing the
same role (being in the same method combination bucket), so "an error
is signalled".

In SBCL, we special case the * method group; for that case, the error
signalling check is elided.  (It's not clear to me what the spec was
trying to do at this point; the "same role" is not unambiguously
defined.  But there you go.)

Christophe
From: Michal Krupka
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <ei1ot4$sr1$1@chiara.aioe.org>
On 2006-10-29 02:10:11 +0200, Christophe Rhodes <·····@cantab.net> said:

> Michal Krupka <·······@mac.com> writes:
> 
>> BTW, I've tried the code on LispWorks, SBCL, and Allegro, and only
>> LispWorks does not lead to error after executing (upgrade-config-file
>> 2). The error is "More than one method of type METHODS with the same
>> specializers." Is there an error in my code?
> 
> Formally, maybe.  Buried in the spec for the long form of
> DEFINE-METHOD-COMBINATION, there is the lovely wording (and I
> apologize for the long and complex quote, but this is what we get to
> play with):
...

Thank you for your explanation. Using the * method group works in SBCL 
indeed. Allegro still signals the error.

Michal
From: Pascal Costanza
Subject: Re: Nonstandard method combinations?
Date: 
Message-ID: <4qjg03Fn2vbeU1@individual.net>
Michal Krupka wrote:
> On 2006-10-29 02:10:11 +0200, Christophe Rhodes <·····@cantab.net> said:
> 
>> Michal Krupka <·······@mac.com> writes:
>>
>>> BTW, I've tried the code on LispWorks, SBCL, and Allegro, and only
>>> LispWorks does not lead to error after executing (upgrade-config-file
>>> 2). The error is "More than one method of type METHODS with the same
>>> specializers." Is there an error in my code?
>>
>> Formally, maybe.  Buried in the spec for the long form of
>> DEFINE-METHOD-COMBINATION, there is the lovely wording (and I
>> apologize for the long and complex quote, but this is what we get to
>> play with):
> ...
> 
> Thank you for your explanation. Using the * method group works in SBCL 
> indeed. Allegro still signals the error.

If you really want your method combination to work in spite of the vague 
restrictions of define-method-combination, you can instead override 
compute-effective-method in the CLOS MOP. There, no additional checks 
are performed. The result must look similar to what a 
definition-method-combination has to produce.

Except that Allegro doesn't use user-defined methods on 
compute-effective-method. :(

Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/