[squeak-dev] Re: A criticism of the Nile paper (was Re: My view on Traits)

Andreas Raab 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:
>> NSMetrics>>methodsInClassAndMetaclass:methodListBlock:
> I haven't find a place where it was a problem. If you do, please tell
> me.

Me neither. It just struck me as a potential source of troubles.

>> NSMetrics>>numberOfReimplementedMethodsForClasses:
> 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 
right traits."

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 
some entity)

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 + 
>> NSPositionableStream>
>>    NSCollectionStream
> 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 
NSTPuttablePositionable either).

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

   - Andreas

More information about the Squeak-dev mailing list