[squeak-dev] Re: A criticism of the Nile paper (was Re: My view on
andreas.raab at gmx.de
Tue Jun 3 05:56:16 UTC 2008
Hi Damien -
Sorry this is going to be lengthy, but you are raising an interesting
set of issues so here we go.
Damien Cassou wrote:
> I haven't find a place where it was a problem. If you do, please tell
Me neither. It just struck me as a potential source of troubles.
> I need to have a deeper look into this. Thanks for pointing me. If you
> already have a fix, could you please sent it?
Unfortunately, I don't. (I looked over the relevant results manually
because I was surprised about some of them and then noticed the issues)
> In the paper, I compared the existing design of Squeak and Nile. In my
> opinion it was fair in this respect.
I don't want to split hairs so I'll be blunt. I don't think it was fair
to compare the two because of the conclusions you draw from the
comparison, for example:
"Nile has 40% less methods and 39% less bytecodes than the
corresponding Squeak collection-based stream library. This means we
avoided reimplementation of a lot of methods by putting them in the
This is at the very best misleading since it implies that the difference
in those measures is due to applying traits. The reality is that you
used traits *and* made other refactorings, refactorings that are every
bit as applicable to a non-traits implementation. And the measurable
amount of improvements that can be directly attributed to traits is
precisely zero. A fair conclusion would be to say:
"Nile has 40% less methods and 39% less bytecodes than the
corresponding Squeak collection-based stream library. However, this is
the result of applying several refactorings and cannot be directly
attributed to the use of traits."
> In your version, you compare something that does not exist and is *not*
> functionality equivalent. With your design, you can't simply implement
> the clients we present in the paper.
Well, let's be careful here. Since I posted the code how can you claim
it doesn't exist? InternalStream is *exactly* equivalent in
functionality to NSCollectionStream so when you combine it with Stream
and PositionableStream you have a hierarchy that is *exactly* equivalent
in functionality to Nile's internal stream hierarchy.
As a result, I was taking the measures for exactly those parts that you
chose in the paper, no more no less (there are no comparisons of other
clients in the paper either - they all refer to the internal stream
hierarchy and I compared the precise same set of functionality).
> You could also have reimplemented
> Stream and PositionableStream in InternalStream and have better
> metrics :-).
Yes, and it would have been utterly pointless, too ;-) I didn't want
such a comparison, I wanted one where the application of the same
refactoring that merges Read/Write/ReadWriteStream is done with the same
intent as the existing collection hierarchy.
> I understand that it would be much work and you probably
> don't want to do it. This discussion clearly indicates that my metrics
> are not self-contained. It would be interesting to have another line to
> show the "reusability" of both systems. However, I don't know what can
> be calculated to show that :-). If you have an opinion, please tell me.
I don't have an exhaustive answer but a couple of measures that look
both at cost and benefit might be:
* "Explicit duplication" of code: How much code is explicitly duplicated
in the hierarchy?
* "Total reuse" of code: How much of the code is reused either by being
inherited or used from a trait?
* Explicit requirements: How many required methods must a subclass or
trait user implement to reuse the intended behavior? (btw, your provided
/ required measure is the wrong way around because the "interesting"
measure is how much work you need to do to reap the benefit of reusing
Those would be interesting measures because they can be applied to both
single and multiple inheritance hierarchies statistically and they might
put perspective on some issues. For example: How big of a problem is
explicit code duplication in a single inheritance hierarchy when
compared with the cost of using traits? Question like these, when taken
from a real system comparison could lead to very interesting results.
>> It is interesting to see that the traits version can do without most of
>> those overrides although it isn't clear to me that this would remain a
>> lasting advantage.
> Why? Do you mean Nile currently misses features and adding them might
> break this?
I mean that some of the basics of the Squeak stream hierarchy got put
into place some twenty years ago and some assumptions simply changed
over time. I would expect the same to be true for Nile, in particular if
the quantitative reuse of traits is higher. For example, consider that a
trait defines an operation that raises an exception (#next anyone? ;-)
and that five years later you figured you were wrong and that you'd
rather have it silently return nil. Can you fix that at this point?
Seems *very* unlikely if those traits are widely used. So what happens
is that every "new" user of that trait will start overwriting the method
so you get into precisely the same situation as with the current
collection hierarchy. It seems unavoidable in the long term that some of
these issues will happen (and yes, "in theory" all of that is fixable
but I'm talking about a practical situation).
> The Squeak collection hierarchy needs to be changed also :-D. You
> probably won't agree with a new design based on traits ;-).
Well, I certainly *do* agree with the internal stream refactoring. And
there are others where I'd violently agree that (almost) anything is
better than the current state of affairs (files for example). What I
disagree with is that these refactorings are best applied using traits.
Now, I'll happily admit that the design of Nile is pretty good,
considering that it's using MI. However, I think that a modern
single-inheritance design of streams can be just as effective without
having to resort to the unavoidable complexity of MI solutions. It would
certainly look different; Nile is clearly designed for MI so just
duplicating it would be a useless exercise. But for example, check out
flow for comparison - this is a pretty good example.
>> That said, I would also slightly refactor NSCollectionStream into, e.g.,
>> NSPositionableStream <NSTGettableStream + NSPuttableStream +
> I don't understand your refactoring. Where are NSTGettablePositionable
> and NSTPuttablePositionable? Could you please be more explicit?
Remove them. The idea is to provide extension points so instead of using
the traits you provide (super-) classes as extension points.
NSTGettablePositionable and NSTPuttablePositionable are needless
entities that don't add any value and only make things harder to
understand (and to draw - if you remove them from your picture the
structure becomes significantly simpler). If you want an easy extension
point make it a class and hide all that traits mess in it. If you want
to have uni-directional streams you could add them via class extension
points too (and not need NSTGettablePositionable or
>> The idea in the above refactoring is to keep the "composition class"
>> (NSPositionableStream) separate from the "implementation class"
>> (NSCollectionStream). It really makes it easier to see what you've done
>> in NSCollectionStream and having a class used only to gather the traits
>> also makes it more clear that anything you'd implement at that level
>> really belongs into a trait and not into the class. It makes looking at
>> classes with traits almost bearable ;-)
> I agree and that's what I do at the beginning. However, I didn't wanted
> to add more entities than really necessary.
How interesting. I didn't know that. Doesn't it strike you as at least
somewhat odd that you found that structure to be useful during
development and then got rid of it as "unneeded" scaffolding? If you
found it useful you might consider that other people might find it
More information about the Squeak-dev