[VI4] Multiple Behaviors/Roles proposal

Anthony Hannan ajh18 at cornell.edu
Fri Jun 14 16:31:18 UTC 2002


Hello Squeakers,
	I've been playing around with this idea for a long time, and I think I
finally came up with a decent design for allowing multiple
behaviors/roles per object.  I would like to add this to VI4, what do
you think?

Structure:
	A protocol is an array of selectors.  A behavior is an implementation
of a protocol with its own instance variables.  A behavior can inherit
from another behavior of the same protocol.  A class is a set of
behaviors keyed by protocol.  All behavior instance variables are stored
in sequence in the class's instances, the class knows the offset where
each behavior's vars start.
	A behavior also acts as a pool dictionary where global vars are looked
up.  A pool dictionary is a environment dictionary that holds named
objects and inherits from zero or more other pool dictionaries.  The
super behavior of a behavior is automatically considered an inherited
pool dictionary of the behavior.  The compiler looks up sent selectors
in all protocols found in the behavior's extended pool.  If more than
one protocol contains the same selector name the user chooses which one
he intends.

Method Lookup:
	The selector's protocol is stored in the literals while the selector's
index within the protocol is encoded in the send bytecode.  The protocol
is looked up in the hash table of the class to find the corresponding
behavior.  Then the selector index is used to directly access the
correct method from the behavior.  Every behavior holds the entire set
of methods for its protocol.  So a subclass behavior (sub-behavior) will
shallow copy the method array of its super than override any methods it
wants.  (The browser tools will be made to update these copies correctly
when modifying methods).
	This lookup mechanism obviates the need for a method cache, at the
expense of repeating the method array.  I think this is tolerable if our
protocols are not too big, or if we do not have too many different
behaviors for a protocol.  Remember many classes can reuse the exact
same behavior if their behavior for that protocol is the same.  Most
subclasses today only override methods that are intended to be overriden
in the super.  To reduce unnecessarily copying method arrays we could
keep these intended subclass responsibility selectors in a separate
protocol.  This way when overriding them we don't have to copy the
method array for all the other selectors as well.  If we create
protocols along existing method category lines I think we could achieve
this type of partitioning.
	However, if we want to have large protocols along existing class lines,
then I think we would be better off not copying method arrays for
sub-behaviors but rather use method dictionaries and explicit super
behavior chains like we do today and re-introduce the method cache. 
What do you guys think, small or large protocols (copied method arrays
or chained method dictionaries with method cache)?
	To continue with method lookup: If the selector's protocol is not found
in the class then the protocol's default behavior is executed.  This way
every class does not have to hold every default behavior it wants to
respond to, particularly Object and its many protocols.  Most low-level
default methods will execute 'self subclassResponsibility', so
unimplemented methods will still be found (maybe just a few calls
later).  But having default behavior makes inheritance simpler, you only
have to implement low-level methods to get full functionality without
explicitly specifying which functionality you want to allow.  For
example, all you have to do is implement a method for Collection.do: and
any other default methods that use Collection.do: will work, without you
knowing which behaviors they are and explicitly inheriting from them.
	Super sends can only be sent to messages in the same protocol.  They
are implemented by storing the super method at the end of the method
array and using a normal self message to it.  Additional supers within
supers must also be appended.

Method Lookup Optimizations:
	The behavior of the current method is stored in the frame, so messages
of the same protocol sent to self can be immediately looked up in this
behavior without the need to go through the class hash table.
	The offset for instance vars of the current behavior are also stored in
the frame so the behavior's inst vars can be found immediately.  The
offset for each behavior in a class is stored next to the behavior in
the hash table.
	To speed up common protocol lookup, each class has a primary protocol. 
The selector's protocol is first tested against this primary protocol
and if they match then its behavior is immediately returned and its inst
var offset is set to 1.  If its not then the normal hash lookup is used.

What do you guys think?

Cheers,
Anthony



More information about the Squeak-dev mailing list