From: Marc BATTYANI
Subject: Generic function efficiency
Date: 
Message-ID: <5965s8$hak$1@mhafc.production.compuserve.com>
Hi,

I'm using methods with eql specifiers. 
Is this handled efficiently or should I use a case instead?

example:
I have about 40 actions

(defmethod Process ((obj object) (action (eql action1)))
..)

(defmethod Process ((obj object) (action (eql 'action2)))
..)

or 

(defmethod Process ((obj object) action))
(case action
   (action1 (...))
   (action2 (...))
   ...))

Thanks
Marc Battyani

From: Martin Cracauer
Subject: Re: Generic function efficiency
Date: 
Message-ID: <1996Dec18.100600.15076@wavehh.hanse.de>
Marc BATTYANI <··········@CompuServe.COM> writes:

>Hi,

>I'm using methods with eql specifiers. 
>Is this handled efficiently or should I use a case instead?

>example:
>I have about 40 actions

>(defmethod Process ((obj object) (action (eql action1)))
>..)

>(defmethod Process ((obj object) (action (eql 'action2)))
>..)

>or 

>(defmethod Process ((obj object) action))
>(case action
>   (action1 (...))
>   (action2 (...))
>   ...))

That is definitivly implementation-dependent.

I'd suspect that an implementation with a good compiler and a weak
CLOS implemention (like CMUCL) handles the latter better.

Why don't write a short benchmark and post it together with some
results :-)?

Martin
--
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
···············@wavehh.hanse.de http://cracauer.cons.org  Fax.: +4940 5228536
"As far as I'm concerned,  if something is so complicated that you can't ex-
 plain it in 10 seconds, then it's probably not worth knowing anyway"- Calvin
From: Kelly Murray
Subject: Re: Generic function efficiency
Date: 
Message-ID: <599sv1$kj9@sparky.franz.com>
In article <············@mhafc.production.compuserve.com>, Marc BATTYANI <··········@CompuServe.COM> writes:
>> Hi,
>> 
>> I'm using methods with eql specifiers. 
>> Is this handled efficiently or should I use a case instead?

In my (perhaps lonely) opinion, EQL specifiers have no business
as part of an OOPL.  Imagine that, specializing a method down
to an EXACT argument.  CLOS is too powerful for its own good!

>> example:
>> I have about 40 actions
>> (defmethod Process ((obj object) (action (eql action1)))
>> (defmethod Process ((obj object) (action (eql 'action2)))

The method "Process" carries very little information,
and so the code requires an inefficient way to find the information.
What you should consider are defining 40 different methods.
To add another action, you don't change the process method,
but actually define another method.

(defmethod process (object action)
  ;; apply the action directly on the object.
  (funcall action obj))

(defmethod action1 ((obj object)) ...)
(defmethod action2 ((obj object)) ...)


-kelly murray  ···@franz.com
From: Paul Fuqua
Subject: Re: Generic function efficiency
Date: 
Message-ID: <PF.96Dec19142025@elissa.hc.ti.com>
    Date: 18 Dec 1996 22:58:41 GMT
    From: ···@math.ufl.edu (Kelly Murray)

    In my (perhaps lonely) opinion, EQL specifiers have no business
    as part of an OOPL.  Imagine that, specializing a method down
    to an EXACT argument.  CLOS is too powerful for its own good!

The only extensive use of EQL specialisers that I'm familiar with is in
the old CLUE/CLIO system (a user-interface widget set built on CLX --
CLUE is analogous to Xt and CLIO is analogous to Xaw).

There, we used it mainly for conversion methods:  the CONVERT generic
function would specialise both its TYPE and its VALUE arguments, and
sometimes they would be "normal" types or values and sometimes they'd be
keywords or specific characters.  It was actually much clearer to do it
this way than to use a CASE with clauses containing CASEs.  It's also a
lot easier to modify the behavior for certain subtypes.

Performance?  Well, that's another story, and depends on the
implementation.  Explorers handled it okay, PCL (either AKCL or CMUCL)
didn't.  Harlequin's user interface was based on CLUE the last time I
looked, but I don't know if they retained the CONVERT organisation.

Paul Fuqua
Texas Instruments, Dallas, Texas                     ··@hc.ti.com
From: Barry Margolin
Subject: Re: Generic function efficiency
Date: 
Message-ID: <59i77b$bc3@tools.bbnplanet.com>
In article <··········@sparky.franz.com>, Kelly Murray <···@franz.com> wrote:
>In my (perhaps lonely) opinion, EQL specifiers have no business
>as part of an OOPL.  Imagine that, specializing a method down
>to an EXACT argument.  CLOS is too powerful for its own good!

I think one of the reasons they were included was to provide a way to
implement some features of the predecessor Lisp OO facilities.  For
instance, Flavors has :CASE method-combinations, which allow you to specify
that the first argument is a keyword that is used to specify a particular
set of methods.
-- 
Barry Margolin
BBN Planet, Cambridge, MA
······@bbnplanet.com -  Phone (617) 873-3126 - Fax (617) 873-5508
(BBN customers, please call (800) 632-7638 option 1 for support)
From: Francis Leboutte
Subject: Re: Generic function efficiency
Date: 
Message-ID: <32c0a5f6.5902357@news.glo.be>
···@math.ufl.edu (Kelly Murray) wrote:

>In my (perhaps lonely) opinion, EQL specifiers have no business
>as part of an OOPL.  Imagine that, specializing a method down
>to an EXACT argument.  CLOS is too powerful for its own good!

Maybe the definition of constructor functions in the following case is a
good example of using individual methods:

(defgeneric make-compound-object (class-name ...))
(defgeneric make-atomic-object (class-name ...))

(defmethod make-compound-object ((class-name (eql 'substation)) ...)
 ...)

(defmethod make-compound-object ((class-name (eql 'transformer)) ...)
 ...)

Etc.

One advantage I see in this is about software quality: the code is highly
traceable as all the methods may be displayed at once in a method browser.
To compare with:

(defun make-substation (...))
(defun make-transformer (...))
Etc.

Francis

--
Francis Leboutte, Algorithme, Rue de la Charrette 141, 4130 Tilff, Belgium
····@glo.be      http://user.glo.be/~algo     t&fax: +32-(0)4-3883528
From: Barry Margolin
Subject: Re: Generic function efficiency
Date: 
Message-ID: <59osn3$i87@tools.bbnplanet.com>
In article <··········@sparky.franz.com>, Kelly Murray <···@franz.com> wrote:
>In my (perhaps lonely) opinion, EQL specifiers have no business
>as part of an OOPL.  Imagine that, specializing a method down
>to an EXACT argument.  CLOS is too powerful for its own good!

I just thought of another point in favor of EQL specializers: they make it
easy to implement special-case optimizations in an OO-like fashion.  For
instance, consider the following:

(defmethod multiply ((arg1 (eq 0)) arg2) (coerce 0 (type-of arg2)))
(defmethod multiply (arg1 (arg2 (eq 0))) (coerce 0 (type-of arg1)))
(defmethod multiply ((arg1 (eq 1)) arg2) arg2)
(defmethod multiply (arg1 (arg2 (eq 1))) arg1)

Although this isn't "real OO", neither is the likely alternative: a CASE
statement at the beginning of the regular MULTIPLY method.  The only way I
can think of to do this in a really OO way is to define subclasses for 0
and 1, but that's likely to introduce more inefficiency than the above code
is intended to gain (assuming the implementation of EQL specializers is
indeed efficient!).
-- 
Barry Margolin
BBN Planet, Cambridge, MA
······@bbnplanet.com -  Phone (617) 873-3126 - Fax (617) 873-5508
(BBN customers, please call (800) 632-7638 option 1 for support)
From: Kelly Edward Murray
Subject: Re: Generic function efficiency
Date: 
Message-ID: <59vb2p$n6n@sparky.franz.com>
In article <··········@tools.bbnplanet.com>, ······@tools.bbnplanet.com (Barry Margolin) writes:
>> In article <··········@sparky.franz.com>, Kelly Murray <···@franz.com> wrote:
>> >In my (perhaps lonely) opinion, EQL specifiers have no business
>> >as part of an OOPL.  Imagine that, specializing a method down
>> >to an EXACT argument.  CLOS is too powerful for its own good!
>> 
>> I just thought of another point in favor of EQL specializers: they make it
>> easy to implement special-case optimizations in an OO-like fashion.  For
>> instance, consider the following:
>> 
>> (defmethod multiply ((arg1 (eq 0)) arg2) (coerce 0 (type-of arg2)))
>> (defmethod multiply (arg1 (arg2 (eq 0))) (coerce 0 (type-of arg1)))
>> (defmethod multiply ((arg1 (eq 1)) arg2) arg2)
>> (defmethod multiply (arg1 (arg2 (eq 1))) arg1)
>> 

Sure, EQL specializers can be useful.  But is their cost worth it?
They significantly complicate a CLOS implementation, I'd say at least adding
a 25-50% increase in complexity [and thus sources for bugs, test cases, etc].
The CLOS EQL implementation will not be significantly faster 
than doing it yourself at the user-level.
In fact, EQL specializers can be slower, because CLOS only knows they must be EQL,
where the user knows more about the possible values, wherease CLOS must
be able to handle 1 special-case, as well as 10x1000 special cases. 

The general way to special-case an exact argument would be using
an :around method, which I think is actually a cleaner way to
do it, since all the special cases are handled, well, specially.
Yes, you'd have to edit the source code to add another case, which is not good.  
But CLOS is dynamic, so you can replace the method with a new one at runtime.
Note that in this case the faster EQ (vs EQL) test can be used and
the constant value comparisons can likely be compiled into a single compare
instruction vs at least a handful (5-10 times more) 
than the EQL specialization code that would be generated for the above case.

(defmethod multiply :around (arg1 arg2)
  (cond ((eq arg1 0) (coerce 0 (type-of arg2)))
        ((eq arg1 1) arg2)
        ((eq arg2 0) (coerce 0 (type-of arg1)))
        ((eq arg2 1) arg1)
        (t (call-next-method))))


-Kelly Edward Murray  ···@franz.com  http://www.franz.com/
From: Lou Steinberg
Subject: Re: Generic function efficiency
Date: 
Message-ID: <LOU.96Dec25005629@atanasoff.rutgers.edu>
In article <··········@sparky.franz.com> ···@math.ufl.edu (Kelly Murray) writes:

   In my (perhaps lonely) opinion, EQL specifiers have no business
   as part of an OOPL.  Imagine that, specializing a method down
   to an EXACT argument.  CLOS is too powerful for its own good!

Here's another possible example of the use of EQL specifiers, from
some code I wrote as an example of CLOS for the AI Programming course
I teach.  The code as a whole implements fake windows on a
character-only terminal.  Slot `chars' of class `window' holds an
array of chars recording what character is at what place in the
'window' this object represents.  The following methods are used to write
characters into this array.  Most characters are simply inserted, but
a new-line character is handled specially:

;;; window-write - write a single character in window at current position
(defmethod window-write ((w basic-window) (char character))
  (setf (aref (slot-value w 'chars) 
	      (window-x-pos w)
	      (window-y-pos w))
	char)
  (window-incr-pos w))

;;; window-write - handle write of newline specially
(defmethod window-write ((w basic-window) (char (eql #\newline)))
  (window-newline w))

;;; window-newline - go to next line.  
(defmethod window-newline ((w basic-window))
  (with-accessors ((x-pos window-x-pos)
		   (y-pos window-y-pos)
		   (y-size window-y-size))
		  w
		  (setf x-pos 0)
		  ;; if past bottom, wrap around to top
		  (incf y-pos)
		  (if (>= y-pos y-size) (setq y-pos 0)))) 
From: Kelly Murray
Subject: Re: Generic function efficiency
Date: 
Message-ID: <59vc13$n6n@sparky.franz.com>
> The code as a whole implements fake windows on a
> character-only terminal.  Slot `chars' of class `window' holds an
> array of chars recording what character is at what place in the
> 'window' this object represents.  The following methods are used to write
> characters into this array.  Most characters are simply inserted, but
> a new-line character is handled specially:
>
> ;;; window-write - write a single character in window at current position
> (defmethod window-write ((w basic-window) (char character))
>  (setf (aref (slot-value w 'chars) 
>	      (window-x-pos w)
>	      (window-y-pos w))
>	char)
>  (window-incr-pos w))
>
>;;; window-write - handle write of newline specially
>(defmethod window-write ((w basic-window) (char (eql #\newline)))
>  (window-newline w))

In this case, I think it works just as well
to put in the explicit check in the main window-write method:

(defmethod window-write ((w basic-window) char)
  (if (eq char #\newline) 
    then 
     (window-newline w)
    else
     (setf (aref (slot-value w 'chars) 
                 (window-x-pos w)
                 (window-y-pos w))
	   char)
     (window-incr-pos w)
   ))

Or it be done as an :around method also.

(defmethod window-write :around ((w basic-window) char)
  (if (eq char #\newline) 
    then (window-newline w)
    else (call-next-method)))