Navigating traits in 3.9
andreas.raab at gmx.de
Sun Oct 22 20:50:48 UTC 2006
Hi Adrian -
> Anyway, lets go through your problems step by step:
Thanks, it is very much appreciated. BTW, I'm slowly getting a feel for
what exactly seems to be so hard about traits and their relationships
and I'll talk a little more about it below.
>> Browsing the senders of the former netted me 9 places to look at
>> among which were four implementors of #addSelector:withMethod:. And so
>> began the tale of the traits...
> If you had used the OmniBrowser tools as I suggested in the other mail
> you would see that among the 4 implementers of #addSelector:withMethod:
> there are 3 that do not implement the method locally but obtain it from
> a trait. This leaves us with exactly _one_ case to look at:
That's strictly speaking correct, but lacks one critical bit of
information namely what the relationships between the other listed
entities are and whether they are relevant for the user. If this were in
a (single inheritance) class tree, I would want to look at the hierarchy
to figure out which of these places may turn out to be relevant. And
this information (like Behavior using TPureBehavior using
TCompilingBehavior) turned out to be quite important later on. With
"just" looking at TCompilingBehvavior it's like looking at a class
implementing some message without knowing whether that's a superclass
or a subclass of the place you're interested in (because if I understand
it correctly, a "user" of TCompilingDescription could just implement
that method and would show up just like TCompilingDescription). As a
user, right now it feels as if you've found "two interesting places"
without being able to correlate them.
>> Trying to find the users of TAccessingMethodDictDescription leaves us
>> fishing through menus with no success. "class refs" does nothing, so
>> does Cmd-Shift-N
> Of course not. There aren't any references to traits in methods -- just
> as there are usually no references to abstract classes.
> Again, the problem is a missing UI. To get the users of a trait, do
> "TAccessingMethodDictDescription users"
I think this needs fixing. Key relationships have always have menu
entries in the browsers - we don't ask people to print "FooClass
superclass" any more than we ask them to print "FooClass subclasses" to
figure out its dependencies. A simple fix could be to change the wording
from "class refs" to "users of ..." since that works for both classes
and traits and can do the right thing dynamically based on the selection.
Same goes for "show hierarchy" where we really want to print a combined
tree for subclasses and traits to make clear what the relationships are.
[... snip ...]
> For example, between Behavior and TraitBehavior there is one trait named
> TPureBehavior (in this case it saves us from duplicating about 100
> methods). The same way the classes further down the hierarchy share
> traits. So, that's pretty straightforward, isn't it?
Well, as far as that goes, yes. But I'm still confused. Let's see: After
visualizing (e.g., writing down) who depends on what, what I gathered so
far is that TCompilingDescription is used by TPureBehavior which is used
by Behavior and TraitBehavior. And TCompilingDescription itself
implements both #addSelector:withMethod: as well as
Simply put my question is: How does TAccessingMethodDictDescription come
into play here? Why does it implement #addSelector:withMethod:notifying:
given that it is not a user of TCompilingDescription and therefore not
responsible for managing adding/removing selectors (which is all handled
in TCompilingDescription)? If there is a relationship between the two
and that method, were is that relationship stated?
A "second order" question is: Why is it even possible to use traits in
such a conflicting way? Shouldn't there be some way of ensuring that a
protocol that is defined by a trait in a superclass doesn't get
redefined nilly-willy by a trait in a subclass?
Along the same lines, are you aware that TraitDescription uses
TTransformationCompatibility and ClassTrait (being a subclass of
TraitDescription) uses it, too? Is there a point which I'm missing to
that duplication? (I doubt it, in which case it's an interesting data
point about the complexities of understanding relationships with traits
and classes since I'd assume that at least a dozen people have looked at
it and missed that duplication)
> What's probably adding a lot of confusion is that those traits are again
> decomposed into sub-traits. This wasn't done to avoid code duplication
> but rather as an attempt to factor out methods into logical groups. It
> seems that this just adds unnecessary confusion because without good
> tools the decomposition is not obvious.
Yes, that's definitely part of it. The main problem as I see it, is that
you're creating two tree structures, one for the traits using each
other, and one for the class hierarchy, and then you "mix in" selected
nodes of the traits tree into the class tree. And since there are no
requirements about which traits you can mix into which classes it is
perfectly possible to create "logical cycles" where one trait uses
another but in the class hierarchy it's exactly the other way around (a
superclass using the "sub-trait" and the subclass using the
"super-trait"). That's an extreme example but points out the fundamental
problem of mapping these tree structures into each other.
Interestingly, this cannot happen in most MI systems I'm aware about.
Usually the rules of what can be inherited from where are pretty
strictly aligned with the hierarchies (if the ability to inherit from
the same tree exists at all).
> Now, if _this_ does not help you to understand how this all works...
It does. OTOH, it also makes me understand a lot better what I dislike
about traits - it seems to boil down to the same old MI arguments (which
are basically: hard to understand and difficult to use "right" because
of the complexity of the resulting structures).
More information about the Squeak-dev