From: jonathon
Subject: :before, :after, and :around vs call-next-method
Date: 
Message-ID: <1121123201.988028.17440@g14g2000cwa.googlegroups.com>
I'm trying to think of examples when the standard methods would be
better to use than regular inheritance.  Would someone mind posting an
example?

From: Pascal Costanza
Subject: Re: :before, :after, and :around vs call-next-method
Date: 
Message-ID: <3jgfs4Fpet3mU1@individual.net>
jonathon wrote:
> I'm trying to think of examples when the standard methods would be
> better to use than regular inheritance.  Would someone mind posting an
> example?

Don't say "standard methods" in this context. This term has a different 
meaning in CLOS. You mean "before/after/around methods". (Sometimes they 
are also called "demon methods", but this is old-fashioned and was 
mainly used in conjunction with Flavors, AFAICS.)

A simple example are :after methods on setters.

(defmethod (setf point-xy) :after ((obj point) x y)
   (format t "The coordinates of point ~S have changed." obj))

This allows you to introduce notification/observer mechanisms.

A number of illustrative examples are also discussed in the papers at 
http://www.dreamsongs.com/CLOS.html

These papers are recommended reading anyway, especially "Integrating 
Object-Oriented and Functional Programming" and "The Shape of the Design 
Space".



Pascal

-- 
2nd European Lisp and Scheme Workshop
July 26 - Glasgow, Scotland - co-located with ECOOP 2005
http://lisp-ecoop05.bknr.net/
From: David Steuber
Subject: Re: :before, :after, and :around vs call-next-method
Date: 
Message-ID: <87pstlxpeh.fsf@david-steuber.com>
"jonathon" <···········@bigfoot.com> writes:

> I'm trying to think of examples when the standard methods would be
> better to use than regular inheritance.  Would someone mind posting an
> example?

I'm not sure what you mean here.  However, if you write a non-trivial
class that needs to do some fancy initialization, take a look at
SHARED-INITIALIZE.  It is a perfect candidate for an :after method:

(defmethod shared-initialize :after ((m message) slot-names &rest init-args)
  (declare (ignore slot-names init-args))
  (when (and (slot-boundp m 'codes) (slot-boundp m 'cipher))
    (with-slots (codes mirror-codes cipher probability-log) m
      (setf codes (coerce codes 'list)
            cipher (coerce cipher 'list)
            mirror-codes (mapcar #'modadd codes cipher)
            probability-log (+ (logp (aref-l +letter-frequency-hypercube+ codes))
                               (logp (aref-l +letter-frequency-hypercube+ mirror-codes)))))))

You can of course do :before and :around methods on SHARED-INITIALIZE
if you need to.

I don't think it is quite right to call the standard method
combination inheritance in the C++ or Java sense of the term.  Don't
forget there are also a slew of other combination types that are
predefined in CLOS and you can even create your own.

-- 
(when (or hope despair)
  (error "Deal with life as it is."))
From: jonathon
Subject: Re: :before, :after, and :around vs call-next-method
Date: 
Message-ID: <1121370767.498862.29300@o13g2000cwo.googlegroups.com>
David Steuber wrote:
> "jonathon" <···········@bigfoot.com> writes:
> I'm not sure what you mean here.  However, if you write a non-trivial
> class that needs to do some fancy initialization, take a look at
> SHARED-INITIALIZE.  It is a perfect candidate for an :after method:
>
> (defmethod shared-initialize :after ((m message) slot-names &rest init-args)
>   (declare (ignore slot-names init-args))
>   (when (and (slot-boundp m 'codes) (slot-boundp m 'cipher))
>     (with-slots (codes mirror-codes cipher probability-log) m
>       (setf codes (coerce codes 'list)
>             cipher (coerce cipher 'list)
>             mirror-codes (mapcar #'modadd codes cipher)
>             probability-log (+ (logp (aref-l +letter-frequency-hypercube+ codes))
>                                (logp (aref-l +letter-frequency-hypercube+ mirror-codes)))))))
>
> You can of course do :before and :around methods on SHARED-INITIALIZE
> if you need to.

Can't this code simple be placed at the end of the applicable method?
From: Espen Wiborg
Subject: Re: :before, :after, and :around vs call-next-method
Date: 
Message-ID: <m3eka0trho.fsf@montgomery.empolis.no>
"jonathon" <···········@bigfoot.com> writes:
> David Steuber wrote:
>> "jonathon" <···········@bigfoot.com> writes:
>> I'm not sure what you mean here.  However, if you write a non-trivial
>> class that needs to do some fancy initialization, take a look at
>> SHARED-INITIALIZE.  It is a perfect candidate for an :after method:
>>
>> (defmethod shared-initialize :after ((m message) slot-names &rest init-args)
<-snip->
>> You can of course do :before and :around methods on SHARED-INITIALIZE
>> if you need to.
> Can't this code simple be placed at the end of the applicable method?

In most cases you'd like the standard shared-initialize to do its
thing.  You could provide your own method, but then you'd have to
either:

a) do the standard shared-initialize's work yourself (difficult to get
   right, and possibly difficult to make portable)

or

b) arrange for the standard shared-initialize to be called (using
   call-next-method).

When the language provides :after methods, why not use them?
   
A more subtle issue is that you usually want to refer to slots of your
object when doing your fancy initialization; before shared-initialize
has run, your slots are either not bound or may contain bogus values
(e.g. when shared-initialize is called from
update-instance-for-redefined-class).

-- 
Espen Wiborg <·······@grumblesmurf.org>
A shark is the only fish that can blink with both eyes.
From: drkm
Subject: Re: :before, :after, and :around vs call-next-method
Date: 
Message-ID: <ufyuenb13.fsf@fgeorges.org>
Espen Wiborg <·······@grumblesmurf.org> writes:

> In most cases you'd like the standard shared-initialize to do its
> thing.  You could provide your own method, but then you'd have to
> either:

> a) do the standard shared-initialize's work yourself (difficult to get
>    right, and possibly difficult to make portable)

> or

> b) arrange for the standard shared-initialize to be called (using
>    call-next-method).

> When the language provides :after methods, why not use them?

  IMHO, the most important point is not to call the next
'shared-initialize' in this method.  This is simple as calling
'call-next-method', as you said.

  But to ensure than if you define a primary 'shared-initialize'
for a class derived of your, some code will be called *in all
case* (not depending of using 'call-next-method') and in a proper
order (before any code associated with the derived class being
called).

  If you write:

    (defclass A () ())
    (defmethod f :before ((a A))
      ...)

this method will always be called when calling the generic
function 'f' on an instance of 'A' (of derived).  If you write:

    (defclass B () ())
    (defmethod g ((b B))
      ...)

one can write:

    (defclass C (B) ())
    (defmethod g ((c C))
      ;; without using 'call-next-method'
      )

so your code will never be called, or:

    (defclass C (B) ())
    (defmethod g ((c C))
      ...
      (call-next-method))

and your code will be called at a possibly wrong moment.

  Indeed, iall that depends on what you want to do in your
method, and how it's related to inheritance.

--drkm