Traits approaching mainstream Squeak
daniel.vainsencher at gmail.com
Sun Aug 28 21:01:59 UTC 2005
Andreas Raab wrote:
> I think this can be interpreted as an argument for generally "fine
> granularity traits" since it seems that most classes will have multiple
> responsibilities and thus traits would almost always cover only a single
> responsibility for any class (15 responsibilities for ClassDescription
> sounds about right to me).
Well, while Class and friends may be an exception, I would generally shy
from creating a class with 15 responsibilities. Too much responsibility
leads to madness. Maybe it needs to learn to delegate and cooperate more :-)
This, BTW, seems to be a genuine difference of style among Smalltalkers
- some believe that a class should have 1-3 responsibilities, and let
its collaborators do other things without it. It seems that
ClassDescription, while collaborating with the compiler, its methods
categorization, the class categorization, and the filein out code,
treats them as its own responsibilities, with significant amounts of
code in CD devoted to them. I for one would prefer that a class simply
not know where its code comes from. At most it might have a couple of
class extensions delegating to the compiler, but they'd be almost empty
>That would be different from saying traits
> generally cover interfaces (say, "Collection") which typically span
> multiple responsibilities.
I don't know that responsibilities are finer grained than interfaces.
What do you mean by an interface?
> I am not sure I agree here btw.
I'm not sure about this opinion myself. And I would not, at this point,
create traits for responsibilities unless I need to share them. In a
specific context that requires the sharing, I might find myself acting
> Although you are right in such that a
> small trait will typically have a small set of requirements these
> requirements can spread very easily
I don't understand what "spread very easily" means here. I'm also not
sure how it relates to the rest of your argument.
> and (I think) will often lead to a
> unique possible combination of traits to create a functioning entity. If
> that were correct, I would claim that having a more coarse-grained
> representation would be advantageous (see below).
In this case, I would agree with you. One particular example of this
situation would be mutually recursive functions separated into two
traits. Though even there, one might one to have an alternative
implementation of one of them.
However, I'm not sure why you assume that unique feasible combinations
would be common.
> Well, that's the 64k question, isn't it? If all you have is a hammer
> everything looks like a nail, but if you have multiple tools you need to
> find out what to use when and where and how. Like, for example, why not
> implement traits purely using traits? We could (trivially) get rid of
> the abstract superclasses PureBehavior, TraitBehavior, and
> TraitDescription, and just have a single Object subclass: #Trait with
> all the behavior mixed in via traits. What would we gain? What loose?
I think one general gain from Traits is that the question "how do
separate the responsibilities of this class" becomes orthogonal to
"which of those subsets of responsibility am I more likely to reuse in
the future", which would be one reason to make Behavior a superclass to
ClassDescription, rather than the opposite.
I don't yet see a loss. Is there any loss at all for this case? for
class and its superclasses?
>> I for example think that the current inheritance of TraitBehavior and
>> Behavior from PureBehavior is inappropriate, and should by replaced
>> sharing the methods using a Trait usage. The reason for PureBehavior
>> existing in the demo is historical - before Traits started
>> functioning, that way of sharing implementation wasn't available. That
>> change is going to happen before integration with mainstream, among
>> things because it would make the Kernel hierarchy more similar to what
>> is there now, therefore minimization integration costs.
> I see. It would be interesting for me to see an actual design from first
> principles that ignores the restrictions we have right now (like VM
> dependencies) and just tries to build a comprehensive set of
Jecel, maybe you can describe the relevant parts of NeoSmalltalk?
>> I'm not familiar with all of it. Lets discuss something specific, so
>> we can learn from it. Lets look at some specific Trait that annoys you
>> and see how it can be improved.
> Well, to come back to the first part of the message let's look at the
> requirements for traits in ClassDescription and where we find them:
> For TAccessingMethodDictDescription (which I am choosing only because
> it's the first in the category and not too long) we have the following
> -> TMethodDictionaryBehavior
This is a natural requirement
> -> ClassDescription/TraitDescription
> -> TTestingDescription
These are only needed because of #addSelectorSilently:withMethod:, and
its use of the peculiar interface of #noteAddedSelector:meta:. Of the
two non trivial implementations of the latter, only one uses the isMeta
information, so why should it be part of the interface? this interface
by itself mixes two responsibilities. Just omit the isMeta parameter,
send the message to self, instead of self instanceSide, and have what
clients need the information, and wish to assume a dual hierarchy, ask
#isMeta. So one thing we learn is that fine grained Traits can be done
well only if the interfaces and implementation are designed to keep
responsibilities separate. I'm not arguing that that's necessarily
something we should always do BTW, but its worth considering.
The rest is required by the existence of organizations.
> -> ClassDescription/TraitDescription
This is a requirement for a variable, doesn't require a new Trait.
> -> TTraitsCategorisingDescription
> -> TBasicCategorisingDescription
This is interesting. These are required in #removeSelector:, and only
because of the existence of categorization. If I find myself wanting to
use TMethodDictAccessing without method categories, I could certainly do
so, by overriding this single method in the class/trait using it.
Anyway, I'm not sure what the justification is for having two traits
that deal with categorization (TBasic.. and TTraits..).
> In this case support for a tiny trait is typically implemented using 4-5
> further traits
Actually only 3, count em :-), and maybe they should be two.
>(which in turn will use other traits which makes up for
> the full set of traits used in ClassDescription) and I wonder what this
> does to people trying to understand the system.
I think "people trying to understand the system" is too wide. People
trying to understand behavior should probably not concern themselves
with traits at all. Just look at the resulting flat class. For people
trying to understand the dependencies (maybe they want to reuse), the
complex dependencies were already there before the traits. The method
categories we about as fine grained, except they did help because they
have no semantics and no tool support (requires).
So I'd claim that a good composition into Traits, with only slightly
less granularity than there is there now, would be an improvement for
understandability on having only the flat view.
> Understanding or
> changing any of these seems to become a major effort in navigating
> through the various traits and for some reason that disturbs me greatly.
> There is a basic lack of locality of reference - something that has
> always disturbed me in Smalltalk but with traits (or rather: the way
> it's being used right now in the class hierarchy) this may be going to
Actually, trying to track down the information I needed for the analysis
above, I feel exactly what you mean. To support understanding the link,
we really need tool support to tell us "what methods are requiring
this", and "who is providing method in this context".
> Do I know how to improve this? Well, I don't exactly, other than saying
> perhaps we should look at having few "macro traits" representing
> multiple responsibilities instead of "micro traits" for each individual
This is definitely another option worth considering. I would not be
surprised if taste and circumstance would drive both "opposed
approaches" to quite similar results in practice. At least while "no
duplication" is considered a requirement.
> It may also be worthwhile to get away from the goal of
> "maximizing code reuse" and rather "maximize interface consistency". To
> me the latter is more important that the former - I don't mind a bit of
> duplicated code if (for example) it makes it easier to understand the
> system or to reduce dependencies.
I disagree with that - code duplication causes the creation of
inconsistent variations, and that causes bug-fix hell, and destroys
> But one of the most powerful features
> of traits is that we can extend an interface for all classes
> implementing it and can do so in a concrete way - so that a new method
> like Collection>>gather: can be implemented in the Collection trait
> (interface) and will automatically apply to all of the users
> (implementors) of that trait.
Yes, that sounds like a good usage scenario. This is orthogonal to the
granularity though. I could certainly have an "interface" level Trait,
itself constructed from "responsibility" level Traits, and then widening
a responsibility also widens the implemented interface. And the
interface would be understandable as a flat thing, if you don't care
about the division of responsibilities.
> Not really. Virtual categories might work. Coloring might. Even just
> annotating the browser so it lists the trait in the annotation pane
> might work.
Oh, I forgot, that too was in the prototype... :-)
Well, we'll see.
>> > In general, I think it would be helpful if some guidelines could be
>> > established for how to use traits in practice.
>> I think the best we can do at the moment is -
>> A. Study and discuss the existing design examples - the collections
>> restructure (I don't know if the code is available somewhere) and the
>> kernel restructure as seen in the demo.
> I haven't seen the collection code but I'd be interested in it.
papers 1 and 6 have relevant things to say, though they don't give the
code. I don't know if that's available.
>> B. Start playing with these new options in our own work.
>> It's new, like Mike said, the patterns book for Traits has not yet
>> been written...
> Well, I think we have a few good things that we should try, including:
> What if we avoid inheritance completely? (I think that's worth a try
> just to see how it feels) How would a coarse-grain set of traits "feel"?
Yup. Time to experiment. I think it will take about 3 weeks before we
can do the preparatory work for inserting of Traits into 3.9a. Any
bugs/lacks that you all help us find and fix in the demo image till that
time can avoid being seen by all alpha, so you're encouraged to do so.
More information about the Squeak-dev