[comp.lang.smalltalk] implementing nonintrusive procedures

weeks@hpdtl.HP.COM (Gregory Weeks) (09/09/88)

I'm aware that the message-sending paradigm of Smalltalk doesn't smoothly
handle all situations.  There is one situation in particular that I'm
curious about.

Suppose I have a useful procedure in mind that accesses its argument only
by sending it certain messages.  Suppose further that I want to be able to
apply this procedure to any object that recognizes these messages.  There
are many ways to implement such a procedure.

1.  For every target class [that is, for every class that recognizes the
messages used in the procedure], define a method that implements the
procedure.

2.  Arrange somehow for all of the target classes to have a common
superclass and implement the procedure as a superclass method.

3.  Define a block that implements the procedure and bind it to a global
variable.

4.  Implement the procedure as a method of a nontarget class.

Are some of the above choices clearly superior?  Is there an established
Smalltalk style that rules out some of the choices?

hibbert@arisia.Xerox.COM (Chris Hibbert) (09/13/88)

>  weeks@hpdtl.HP.COM (Gregory Weeks)
>  Suppose I have a useful procedure in mind that accesses its argument only
>  by sending it certain messages.  Suppose further that I want to be able to
>  apply this procedure to any object that recognizes these messages.  There
>  are many ways to implement such a procedure.
>  
>  Are some of the choices clearly superior?  Is there an established
>  Smalltalk style that rules out some of the choices?

Yes there are better and worse choices, and yes there is an
established Smalltalk style.  The better choices and Smalltalk style
don't always coincide.  The options you suggest in rough best-to-worst
order:

>  Arrange somehow for all of the target classes to have a common
>  superclass and implement the procedure as a superclass method.

This is the best choice when it's possible.  If you're writing code
from scratch, you want to try very hard to arrange that classes that
should support the same protocol share a superclass.  This often means
creating an "abstract" superclass that isn't designed to have any
instances itself, and instead acts as a template for its subclasses.

When you're adding code to existing classes, or when you can't find a
way to arrange the class hierarchy to satisfy all your objectives, you
have to choose from the other mechanisms.

>  Implement the procedure as a method of a nontarget class.

Here's where "clearly superior" and Smalltalk style diverge.  (That
evaluation may be controversial.)  Smalltalk style says you should put
this kind of functionality in a class unrelated to the behavior like
class SystemDictionary.  Lots of cruft has found its way into
class SystemDictionary over the years.  

A better solution might have been to put this kind of behavior in a
newly created nontarget class.  For instance, all the methods in the
system that deal with memory space could have been put in a
MemoryManager object instead.  If the interface was well designed, it
might be possible to reify the interface and provide a real
MemoryManager object in the interpreter.  Instead, what we have looks
like clutter.

>  1.  For every target class [that is, for every class that recognizes the
>  messages used in the procedure], define a method that implements the
>  procedure.

This solution would be a maintenance nightmare.  When (not if) the
generic procedure needed to be changed, all of the copies would have
to be found and changed.  If some copies of the method had been stored
in files, they wouldn't be found by any change management mechanism
I've seen implemented for Smalltalk.  (Which is not to say that
someone couldn't write such a mechanism.)

>  3.  Define a block that implements the procedure and bind it to a global
>  variable.

This is not recommended at all.  If you do this, then no one will ever
be able to find your code.  (No one ever looks for blocks in the
Global dictionary, and none of the tools in the system are designed to
expect that.)  In addition, finding the source text for a compiled
block is only easy when you're using the browser.


There are other ways of dealing with this, but unfortunately, none of
them match Smalltalk style.  Another possibility is multiple
inheritance.  Several varieties of LISP support multiple inheritance,
in particular ZetaLISP has "flavors", which allows you to "mix-in" a
little bit of behavior from one superclass to several subclasses which
don't need to share storage format.

Chris