Traits approaching mainstream Squeak

Daniel Vainsencher daniel.vainsencher at gmail.com
Sat Aug 27 22:02:21 UTC 2005


Hi Andreas!

Andreas Raab wrote:
> Out of curiosity: I studied the use of traits in the kernel classes and 
> it wasn't clear to me how you decide on the granularity of the traits 
> involved. Can you say more about this?
I haven't done that myself, that's (IIUC) mostly Adrian's work. He's 
best qualified to describe his intentions. I personally definitely agree 
that there are many open questions about how Traits should be used, and 
a serious discussion about they've been used so far would be really helpful.

> For example, I think ClassDescription uses roughly 15 traits, many of 
> which seem only to be used by ClassDescription/TraitDescription so there 
> seems comparadly little reason to (for example) define 
> TCopyingDescription, TCompilingDescription, TPrintingDescription etc. 
> separately. Looking over the methods involved it almost feels as if the 
> decision for defining the traits has been done along the lines of 
> protocols / categories. Is this correct? 
Adrian or maybe Stef are better suited to respond to that as a specific 
question. The general question is "What is the proper granularity of 
Traits?" My opinions: methods in a trait should implement a specific 
responsibility in a reusable way. To serve this, they should have a 
small requirements footprint, such that any class/trait that wants an 
implementation for this responsibility has to provide them anyway.

Here's another general question -
In my opinion at the moment, and I'm curious what other people think, in 
the presence of Traits, there is no excuse for separating Behavior, 
ClassDescription, and Class into a hierarchy. The responsibilities each 
represents could be reused separately if they were Traits. So the only 
clearly compelling reasons to create classes become that you want to 
have instances with that shape and behavior. An abstract class doesn't 
seem to make too much sense. So when should we use Traits to replace 
inheritance as a mechanism for sharing implementation? always? Never 
except when needed to avoid duplication or the complexities of delegation?

> In which places have you been 
> avoiding inappropriate inheritance?
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.



> It seems to me that a valid alternative for avoiding inheritance 
> problems is instead of duplicating the Behavior/ClassDescription/Class 
> hierarchy via TraitBehavior/TraitDescription/ClassTrait would be to make 
>  Trait delegate to an appropriate class description (e.g., a trait uses 
> a class description for managing the Behavior side of things). Can you 
> say anything about how such a design would have affected the definition 
> of traits for these kernel classes themself?
IIUC this proposal, it would require creating a class description for 
every Trait. But a ClassDescription inheriting from Behavior know too 
much and does too much - it is a full descriptor for object with 
behavior, which a Trait isn't and doesn't need. So the alternatives 
would be to either factor out ClassDescription responsibilities to a 
delegate that would serve both Classes and Traits, or to share the 
ClassDescription behavior via one Trait, instead of by parallel classes 
that use the same Traits. Delegation doesn't feel to me like the right 
solution because the interface is wide, and there doesn't seem to be an 
opportunity for exchanging the CD with a polymorphic alternative. I 
think the one Trait used directly might be nicer than the two classes 
sharing many Traits. An advantage of the current approach is that it 
makes the smallest change to the structure of the Class hierarchy.

> BTW, the reason I'm asking these questions is to get a better 
> understanding on how one uses traits effectively. Looking over the 
> current uses it struck me that using a large number of fine-grained 
> taits can make it actually harder to understand the system. At least 
> that was my feeling when I looked over the current structure - none of 
> the traits are fully defined internally 
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.

> and it's really hard to go 
> fishing for what you still need to implement to "make it work" (I 
> actually really miss state here - I think it would be much easier if you 
> could see that someone wrote this with having in mind that "X would be 
> an instance variable"). I tried playing with the required browser but it 
> felt non-conclusive to use (it felt like there were random methods shown 
> in the required category).
About the required browser -
1. Did you use the version I announced? an earlier beta had a critical 
bug, though it was in the other direction - sometimes showed too few 
methods.
2. Do note that requirements are a non trivial property to compute 
(including in our heads). One reason is that requirements are often 
inherited. For example, Object implements #stepIn: which self sends 
#step. Therefore, everything in the system that doesn't implement #step 
and does inherits #stepIn: requires #step.

I think this, for example, points to a great example of non optimal use 
of inheritance to share behavior, that can be improved upon using Traits.

Getting back to your questions:
If you find specific cases where the requires browser is not doing what 
you expect it to, please let me know. It hasn't seen that much use, so 
there might be tests we've missed.


> Also, since you are asking for "near term feedback" 
That too :-)

> I think it is 
> critical that one be able to see which methods come from traits and 
> which one's don't. For example, I was curious if Class had any of its 
> own methods or if all of the methods come from the three traits it 
> uses[*] and also which traits are used by which classes (e.g., "all 
> users of TVariablesClass").
Right. In the prototype this happened in two ways: non-locally defined 
methods were green, and under each class that has traits, in addition to 
those, there was a computed entry "-own-" that shows only local methods.

Have a preference?

> [*] Similar question here: What were the design criteria to make Class 
> use three traits that (I think) are not used anywhere else? Which 
> methods were left in class Class and why?
> 
> Just knowing how and why
> you decided to factor the kernel classes the way you did would be a good 
> start. Oh, and comments for the traits might be helpful too ;-)
I'll let Adrian deal with these :-) he's on vacation, btw, so it might 
be a while.

 > 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.
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...

Keep up the good questions, and proposed patches welcome.

Daniel



More information about the Squeak-dev mailing list