From: Rob St. Amant
Subject: CLOS question
Date: 
Message-ID: <f8j0o8$8qc$1@blackhelicopter.databasix.com>
I have what seems like a familiar CLOS question, but a quick Google
search doesn't turn up an answer: I have a method, m, and an after
method for it.  The after method is specialized on class c.  I'd like
to define a class d that inherits c, and have instances of d behave
just like instances of c *except* for that single after method.

I don't believe that I can prevent the after method for class c from
being run when m is called on an instance of d.  (Though it would be
convenient if I were wrong about this.)  My question is about the
choices for design.  I could break class c into two classes, c1 and
c2, and only define the after method for c2, and have d inherit c1,
but I'm wondering if there are other reasonable designs.

From: Scott Burson
Subject: Re: CLOS question
Date: 
Message-ID: <1185746464.124847.288820@j4g2000prf.googlegroups.com>
On Jul 29, 2:27 pm, ·······@ncsu.edu (Rob St. Amant) wrote:
> I have what seems like a familiar CLOS question, but a quick Google
> search doesn't turn up an answer: I have a method, m, and an after
> method for it.  The after method is specialized on class c.  I'd like
> to define a class d that inherits c, and have instances of d behave
> just like instances of c *except* for that single after method.

This suggests to me that you have analyzed your domain incorrectly.
It would seem that D is not really a subclass of C if you don't want
it to inherit the full behavior of M.  Indeed, based on your last
sentence, it sounds like it's the other way around: C is a subclass of
D!

Of course you've left out a lot of information in order to abstract
the problem, so I don't know if this is really correct.  But it's the
kind of question I would ask.

-- Scott
From: Rob St. Amant
Subject: Re: CLOS question
Date: 
Message-ID: <f8j3dv$fp3$1@blackhelicopter.databasix.com>
Scott Burson <········@gmail.com> writes:

> On Jul 29, 2:27 pm, ·······@ncsu.edu (Rob St. Amant) wrote:
>> I have what seems like a familiar CLOS question, but a quick Google
>> search doesn't turn up an answer: I have a method, m, and an after
>> method for it.  The after method is specialized on class c.  I'd like
>> to define a class d that inherits c, and have instances of d behave
>> just like instances of c *except* for that single after method.
>
> This suggests to me that you have analyzed your domain incorrectly.
> It would seem that D is not really a subclass of C if you don't want
> it to inherit the full behavior of M.  Indeed, based on your last
> sentence, it sounds like it's the other way around: C is a subclass of
> D!
>
> Of course you've left out a lot of information in order to abstract
> the problem, so I don't know if this is really correct.  But it's the
> kind of question I would ask.

Hmm. . .  It turns out that there's a strong sense in which you're
right about this (which I didn't realize): I've come across this issue
in writing some software for part of an AI programming course.  The
general approach involves building a simple system and showing how it
can be extended, incrementally, to gain more functionality or
generality.  It does seem reasonable to say, "Okay, we can get so far
with class C, but it has limited generality.  Let's see what we can do
if we build D from scratch, applying what we've learned.  C will end
up being a special case of D."  Thanks for the thought.
From: Scott Burson
Subject: Re: CLOS question
Date: 
Message-ID: <1185749320.165554.297470@m37g2000prh.googlegroups.com>
On Jul 29, 3:13 pm, ·······@ncsu.edu (Rob St. Amant) wrote:
> It does seem reasonable to say, "Okay, we can get so far
> with class C, but it has limited generality.  Let's see what we can do
> if we build D from scratch, applying what we've learned.  C will end
> up being a special case of D."  Thanks for the thought.

You're quite welcome.  It's also possible, of course, that C will turn
out not to be exactly a special case of D, in which case they'll be
siblings or cousins or something, as you originally suggested.

-- Scott
From: Rainer Joswig
Subject: Re: CLOS question
Date: 
Message-ID: <joswig-453135.00094030072007@news-europe.giganews.com>
In article <························@j4g2000prf.googlegroups.com>,
 Scott Burson <········@gmail.com> wrote:

> On Jul 29, 2:27 pm, ·······@ncsu.edu (Rob St. Amant) wrote:
> > I have what seems like a familiar CLOS question, but a quick Google
> > search doesn't turn up an answer: I have a method, m, and an after
> > method for it.  The after method is specialized on class c.  I'd like
> > to define a class d that inherits c, and have instances of d behave
> > just like instances of c *except* for that single after method.
> 
> This suggests to me that you have analyzed your domain incorrectly.
> It would seem that D is not really a subclass of C if you don't want
> it to inherit the full behavior of M.

Why that? Each more specific primary method blocks the
less specific primary method. Thus you are not inheriting
the whole functionality. Sure, you can call the next method,
but by default this is not done. Overwriting less specific
functionality is a capability provided for primary
methods. Just not for :before and :after methods. But those
were design decisions for the standard method combination.

>  Indeed, based on your last
> sentence, it sounds like it's the other way around: C is a subclass of
> D!
> 
> Of course you've left out a lot of information in order to abstract
> the problem, so I don't know if this is really correct.  But it's the
> kind of question I would ask.
> 
> -- Scott
From: Scott Burson
Subject: Re: CLOS question
Date: 
Message-ID: <1185747319.410983.157740@d30g2000prg.googlegroups.com>
On Jul 29, 3:09 pm, Rainer Joswig <······@lispmachine.de> wrote:
> In article <························@j4g2000prf.googlegroups.com>,
>  Scott Burson <········@gmail.com> wrote:
>
> > This suggests to me that you have analyzed your domain incorrectly.
> > It would seem that D is not really a subclass of C if you don't want
> > it to inherit the full behavior of M.
>
> Why that? Each more specific primary method blocks the
> less specific primary method. Thus you are not inheriting
> the whole functionality. Sure, you can call the next method,
> but by default this is not done. Overwriting less specific
> functionality is a capability provided for primary
> methods. Just not for :before and :after methods. But those
> were design decisions for the standard method combination.

If D is a subclass of C, then although D may override a primary method
of C, D's method should maintain all the invariants that C's method
does.  Demon methods are a handy way of maintaining invariants.  It
doesn't sound right to me that C would have some invariant that it is
maintaining with a demon method, and yet that invariant doesn't apply
to one of its subclasses.

-- Scott
From: Rainer Joswig
Subject: Re: CLOS question
Date: 
Message-ID: <joswig-E527A4.00190430072007@news-europe.giganews.com>
In article <························@d30g2000prg.googlegroups.com>,
 Scott Burson <········@gmail.com> wrote:

> On Jul 29, 3:09 pm, Rainer Joswig <······@lispmachine.de> wrote:
> > In article <························@j4g2000prf.googlegroups.com>,
> >  Scott Burson <········@gmail.com> wrote:
> >
> > > This suggests to me that you have analyzed your domain incorrectly.
> > > It would seem that D is not really a subclass of C if you don't want
> > > it to inherit the full behavior of M.
> >
> > Why that? Each more specific primary method blocks the
> > less specific primary method. Thus you are not inheriting
> > the whole functionality. Sure, you can call the next method,
> > but by default this is not done. Overwriting less specific
> > functionality is a capability provided for primary
> > methods. Just not for :before and :after methods. But those
> > were design decisions for the standard method combination.
> 
> If D is a subclass of C, then although D may override a primary method
> of C, D's method should maintain all the invariants that C's method
> does.

'should'. That's a design decision.

There is nothing in CLOS that enforces or requires that.

>  Demon methods are a handy way of maintaining invariants.  It
> doesn't sound right to me that C would have some invariant that it is
> maintaining with a demon method, and yet that invariant doesn't apply
> to one of its subclasses.
> 
> -- Scott

-- 
http://lispm.dyndns.org
From: Scott Burson
Subject: Re: CLOS question
Date: 
Message-ID: <1185748780.368560.97210@x35g2000prf.googlegroups.com>
On Jul 29, 3:19 pm, Rainer Joswig <······@lisp.de> wrote:
> In article <························@d30g2000prg.googlegroups.com>,
>  Scott Burson <········@gmail.com> wrote:
> > If D is a subclass of C, then although D may override a primary method
> > of C, D's method should maintain all the invariants that C's method
> > does.
>
> 'should'. That's a design decision.
>
> There is nothing in CLOS that enforces or requires that.

Indeed.  I was answering not the question he was asking, but the
question I thought he _should_ be asking.  Wasn't that obvious?
(Actually, I spoke of a possible error in domain _analysis_ -- a stage
that logically even precedes design.)

That said -- the principle that subclasses should only strengthen
invariants, not weaken them, is fundamental to object-oriented
programming.  I can't recall ever seeing a case where the principle
was violated that didn't cause problems.  So it's a very very very
strong "should".

-- Scott
From: Barry Margolin
Subject: Re: CLOS question
Date: 
Message-ID: <barmar-8C7D25.22150730072007@newsgroups.comcast.net>
In article <····························@news-europe.giganews.com>,
 Rainer Joswig <······@lispmachine.de> wrote:

> In article <························@j4g2000prf.googlegroups.com>,
>  Scott Burson <········@gmail.com> wrote:
> 
> > On Jul 29, 2:27 pm, ·······@ncsu.edu (Rob St. Amant) wrote:
> > > I have what seems like a familiar CLOS question, but a quick Google
> > > search doesn't turn up an answer: I have a method, m, and an after
> > > method for it.  The after method is specialized on class c.  I'd like
> > > to define a class d that inherits c, and have instances of d behave
> > > just like instances of c *except* for that single after method.
> > 
> > This suggests to me that you have analyzed your domain incorrectly.
> > It would seem that D is not really a subclass of C if you don't want
> > it to inherit the full behavior of M.
> 
> Why that? Each more specific primary method blocks the
> less specific primary method. Thus you are not inheriting
> the whole functionality. Sure, you can call the next method,
> but by default this is not done. Overwriting less specific
> functionality is a capability provided for primary
> methods. Just not for :before and :after methods. But those
> were design decisions for the standard method combination.

IMHO, :BEFORE and :AFTER methods were created primarily to support the 
mixin style of OO programming.  If you don't want to inherit these 
methods, you simply don't mix the parent class into your class.

-- 
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: Kent M Pitman
Subject: Re: CLOS question
Date: 
Message-ID: <uzm1dfem8.fsf@nhplace.com>
Barry Margolin <······@alum.mit.edu> writes:

> IMHO, :BEFORE and :AFTER methods were created primarily to support the 
> mixin style of OO programming.  If you don't want to inherit these 
> methods, you simply don't mix the parent class into your class.

That's a reasonable point of view, certainly.  Though often the
problem is with some particular method and that can mean giving up an
awful lot of other stuff you don't want to give up.

I've personally found this inability to refine or enhance an inherited
:after or :before method a pain, and I wanted to just say so lest
someone think they were irrational for being annoyed.  It's sometimes
indeed a pain ... even though it's also equally possible for someone
writing a method to take some comfort in your lack of easy ability to
overcome their method.  It's a kind of "unstoppable force meets
impenetrable barrier" problem, with arguments on both sides.

It seems to me, though I haven't tried, that it would be possible to
write a method combination that worked differently, perhaps allowing a
:shadowed-after qualifier that, if used, would cause the inherited
:after methods to appear only under a call-next-method.  Or perhaps
a complementary tactic: just using :after but providing a way to do
(skip-next-method) or (skip-next-methods n) or something like that.

Maybe skipping :before or :after methods will accidentally fail to do
important stuff, but then, so does failing to call call-next-method if
all the computation is being done in the primary, and people mostly
work that out... each paradigm trades off the issues of functionality,
safety, convenience, modularity, etc. in different ways.

I see no reason for someone not to try something like that out, and
advertise it for others to use if they find they get something that
feels good.  The language is full of opportunities to experiment with
things like this.
From: Pascal Costanza
Subject: Re: CLOS question
Date: 
Message-ID: <5hav95F3isnpcU2@mid.individual.net>
Kent M Pitman wrote:

> It seems to me, though I haven't tried, that it would be possible to
> write a method combination that worked differently, perhaps allowing a
> :shadowed-after qualifier that, if used, would cause the inherited
> :after methods to appear only under a call-next-method.  Or perhaps
> a complementary tactic: just using :after but providing a way to do
> (skip-next-method) or (skip-next-methods n) or something like that.

Yes, it's possible. In AspectL, you get a method combination for 
"special" functions in which you can specify things like:

(defmethod foo :override :before (...) ...)

...which means that the :before method on the same list of specializers 
is skipped. It's probably also possible to decide this dynamically.

However, I think the end result is too confusing.

(In general, I don't recommend using AspectL for anything else but 
curiosity.)


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: Rainer Joswig
Subject: Re: CLOS question
Date: 
Message-ID: <joswig-4ACF2B.23435629072007@news-europe.giganews.com>
In article <············@blackhelicopter.databasix.com>,
 ·······@ncsu.edu (Rob St. Amant) wrote:

> I have what seems like a familiar CLOS question, but a quick Google
> search doesn't turn up an answer: I have a method, m, and an after
> method for it.  The after method is specialized on class c.  I'd like
> to define a class d that inherits c, and have instances of d behave
> just like instances of c *except* for that single after method.
> 
> I don't believe that I can prevent the after method for class c from
> being run when m is called on an instance of d.

Right. All :after methods will be executed.

>  (Though it would be
> convenient if I were wrong about this.)  My question is about the
> choices for design.  I could break class c into two classes, c1 and
> c2, and only define the after method for c2, and have d inherit c1,
> but I'm wondering if there are other reasonable designs.

Then you get to a more Mixin-style of programming.

Other options:

* use another method combination, maybe write your own one

* put the functionality into a different generic function m1 and
  call that from just one :after method.
  You can then write different primary methods for m1 and only
  the most specific will be called.
From: Rob St. Amant
Subject: Re: CLOS question
Date: 
Message-ID: <f8j2o6$dgt$1@blackhelicopter.databasix.com>
Rainer Joswig <······@lispmachine.de> writes:

> * put the functionality into a different generic function m1 and
>   call that from just one :after method.
>   You can then write different primary methods for m1 and only
>   the most specific will be called.

This hadn't occurred to me but should do nicely in my situation, I
think.  Thanks!
From: Alan Crowe
Subject: Re: CLOS question
Date: 
Message-ID: <86vec124s8.fsf@cawtech.freeserve.co.uk>
Rainer Joswig <······@lispmachine.de> writes:

> In article <············@blackhelicopter.databasix.com>,
>  ·······@ncsu.edu (Rob St. Amant) wrote:
> 
> > I have what seems like a familiar CLOS question, but a quick Google
> > search doesn't turn up an answer: I have a method, m, and an after
> > method for it.  The after method is specialized on class c.  I'd like
> > to define a class d that inherits c, and have instances of d behave
> > just like instances of c *except* for that single after method.
> > 
> > I don't believe that I can prevent the after method for class c from
> > being run when m is called on an instance of d.
> 
> Right. All :after methods will be executed.
> 
> >  (Though it would be
> > convenient if I were wrong about this.)  My question is about the
> > choices for design.  I could break class c into two classes, c1 and
> > c2, and only define the after method for c2, and have d inherit c1,
> > but I'm wondering if there are other reasonable designs.
> 
> Then you get to a more Mixin-style of programming.
> 
> Other options:
> 
> * use another method combination, maybe write your own one

It might look like this:

#| frame method combination runs the most specific :open method if any, the
most specific primary method, and the most specific :close method if any, 
finally return the result of the primary method. Less specific primary methods
are runnable via call next method.

The key words are :open and :close leaving :before and :after available for
use in a more elaborate version.  |#

(define-method-combination frame ()
  ((open (:open))
   (primary () :required t)
   (close (:close)))
  `(progn
    (print (list ,(length open) ;remove once debugged
            ,(length primary)
            ,(length close)))
    ,(if open
         `(call-method ,(first open))
         nil)
    (prog1
        (call-method ,(first primary)
                     ,(rest primary))
      ,(if close
           `(call-method ,(first close))
           nil))))

(defclass c ()())
(defclass d (c)())

(defgeneric act (object)
  (:method-combination frame)
  (:method ((o c))
    (format t "~&Primary method for class C actually working on ~A."
            (class-of o))
    'C)
  (:method :close ((o c))
    (format t "~&Close class C, called on class ~A."
           (class-of o)))
  (:method ((o d))
    (format t "~&Primary method for class D called on ~A."
                   (class-of o))
           (if (y-or-n-p "Call next method? ")
               (call-next-method)
               'that-all-folks)))

With these definitions the close method acts as an after
method,

CL-USER> (act (make-instance 'c))

    (0 1 1) 
    Primary method for class C actually working on #<STANDARD-CLASS C {48B7EBC5}>.
    Close class C, called on class #<STANDARD-CLASS C {48B7EBC5}>.

    C

we still get the return value from the primary method

The frame combination allows you to call next method

CL-USER> (act (make-instance 'd))

    (0 2 1) 
    Primary method for class D called on #<STANDARD-CLASS D {48BA6EB5}>.
    Call next method? y

    Primary method for class C actually working on #<STANDARD-CLASS D {48BA6EB5}>.
    Close class C, called on class #<STANDARD-CLASS D {48BA6EB5}>.

    C

Or not
 
CL-USER> (act (make-instance 'd))

    (0 2 1) 
    Primary method for class D called on #<STANDARD-CLASS D {48BA6EB5}>.
    Call next method? n

    Close class C, called on class #<STANDARD-CLASS D {48BA6EB5}>.

    THAT-ALL-FOLKS

The point of it is to be able to turn off less specific
close methods

(defmethod act :close ((o d)) nil) ;no closing action for class D

CL-USER> (act (make-instance 'd))

    (0 2 2) 
    Primary method for class D called on #<STANDARD-CLASS D {48BA6EB5}>.
    Call next method? n

    THAT-ALL-FOLKS

The close method has been turned off for D, but its still
there for C:

CL-USER> (act (make-instance 'c))

    (0 1 1) 
    Primary method for class C actually working on #<STANDARD-CLASS C {48B7EBC5}>.
    Close class C, called on class #<STANDARD-CLASS C {48B7EBC5}>.

    C


Alan Crowe
Edinburgh
Scotland
From: Rob St. Amant
Subject: Re: CLOS question
Date: 
Message-ID: <f8ljig$b9v$1@blackhelicopter.databasix.com>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:

> It might look like this:
>
> #| frame method combination runs the most specific :open method if any, the
> most specific primary method, and the most specific :close method if any, 
> finally return the result of the primary method. Less specific primary methods
> are runnable via call next method.
>
> The key words are :open and :close leaving :before and :after available for
> use in a more elaborate version.  |#

Cool, thanks.  I'll have to study the code a bit to understand it.