Behavior design options (was: Behaviors vs Modules)

Anthony Hannan ajh18 at cornell.edu
Tue Feb 26 06:53:45 UTC 2002


Nathanael <n.schaerli at gmx.net> wrote:
> I'm really curious what kind of a mixin concept you have in mind.

Well, lets look at different designs from simple to complex:

 A :=  Just single-inheritance classes.  (today's Squeak)
 B :=  A + default methods (each selector has a default method that will
execute if the receiver's hierarchy does not define a method for that
selector.  Default methods cannot access instance variables, they only
can send messages).
 C :=  A + protocols (a protocol is a group of selectors, providing
selector namespace.  Each selector belongs to one and only protocol. 
Selectors in different protocols can have the same name, but they are
considered different selectors, meaning the sender has to choose which
one he means if its not obvious).
 D :=  A + mixins (a mixin is a set of methods that may be shared among
different classes.  Like default methods, mixin methods cannot access
instance variables).
 E :=  B + C.
 F :=  C + D.
 G :=  B + C + D.
 H :=  Object roles (ie. PIE).
 I :=  Multiple-inheritance classes.

I encourage people to vote for one they like the best (with an
explanation of course) and/or add new designs to this list.

My opinion:
	Ambiguities arise in designs C and higher that require even more design
to disambiguate.  Protocols require senders to sometimes use prefixes. 
Mixins require the importing class to resolve conflicts if the mixin
implements a selector already included in the class or another imported
mixin.  Roles and multiple-inheritance have similar issues.
	So I vote for B.  I like B over A because it allows reuse of methods
outside of the single-inheritance structure and gives Squeak some
function orientation, which is closer to reality.  80% of all selectors
in the image have only one method definition (only 20% are polymorphic)
[1].  The default methods can be grouped by functionality (modules) that
often intersect many classes, but are usually executed together.
	However, having no class or receiver type associated with default
methods will make it hard to figure out which receivers they are
intended for.  Protocols would solve this problem by associating a
"protocol type" with the receiver.  But, beside the cost of occasional
message prefixes, it would require us to come up with many new standard
names (protocol names) one per receiver type per module.  For example,
the "exceptions" module would contain additional methods for blocks,
context, and general objects.  We wouldn't want to add #on:do: to the
existing "blockEvaluation" protocol (#value, etc.), instead we would
want to add it to a new "blockExceptions" protocol.  This prolification
of protocols would be overkill.  Instead, I propose we add the receiver
as a variable in the method header, so we can name it something
appropriate like we do with arguments.  For example, here is how I would
define the default method #ifError:

block ifError: errorHandler
	^ block on: Error do: errorHandler

"block" is added to the front of the method header and replaces self in
the code.  Of course this would be optional.  But if default methods
include receiver variables then it will be easier to tell what kind of
receiver is expected without actually tying it to a specific class or
using protocols.  Receiver variables, like argument variables, serve as
informal types.  Informal is a plus since formal types are overkill (as
all Smalltalkers know).  Tools could filter or sort default methods by
the receiver variable, and they can even trace senders and implementors
to figure out which default methods are applicable to which classes.
	Default methods provide reuse of outside-the-hierarchy behavior like
mixins, reducing the need for mixins.  So I would keep the behavior
design simple with just functions and classes that may override those
functions.
	Note, in my original post I proposed design E: default methods +
protocols.  This was because I thought protocols could serve as modules.
 But now I favor transactions/changesets as modules, so protocols are
not worth their weight anymore.  (Double Note, I still haven't studied
SmallScript or PIE yet, which may change my opinion again :-).

Cheers,
Anthony

Footnotes [1]
Calculation of % of polymorphic selectors (methods with the same
selector are counted as polymorphic even if they are not intended to be
polymorphic):
  | poly all |
  poly _ IdentitySet new.
  all _ IdentitySet new.
  Smalltalk allBehaviorsDo: [:cls |
	cls methodDict keysDo: [:sel |
		(all includes: sel) 
			ifTrue: [poly add: sel]
			ifFalse: [all add: sel]]].
  (poly size / all size) asFloat  "=> 0.224"



More information about the Squeak-dev mailing list