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/
"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."))
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?
"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