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
|