Hi Andreas,
On Sat, 2008-05-17 at 13:32 -0700, Andreas Raab wrote:
Damien Cassou wrote:
I'm sorry. I haven't updated the universe package for some time. Please use SqueakSource and load Nile-All. This package depends on other required package and contains NSMetrics.
I've updated the Universe.
Okay, after loading this I understand better where the numbers come from. First, a couple of comments on NSMetrics: #methodsInClassAndMetaclass:methodListBlock: does a union of methods in class and metaclass which looks a little questionable to me. I don't think it matters here but it seems odd to count a method in class and metaclass only once.
I haven't find a place where it was a problem. If you do, please tell me.
The #numberOfReimplementedMethodsForClasses: also has two problems in such that it does only look at methods overridden in the direct superclass (so it doesn't find methods implemented in Stream and overridden in ReadStream but not in PositionableStream) and that it excludes the required selectors of traits but not those of superclasses (i.e., self subclassResponsibility) which it should discount as well (see note below on the metrics that are affected by it).
I need to have a deeper look into this. Thanks for pointing me. If you already have a fix, could you please sent it?
That said, we can now devise a comparison which is more appropriate for a Nile vs. Squeak comparison. I've attached a simple class InternalStream which as a subclass of PositionableStream implements the same folding of ReadStream, WriteStream, and ReadWriteStream. I believe it to be a fully functioning equivalent to NSCollectionStream. If we run the design metrics using InternalStream instead of the three other classes we end up with metrics that look like this (slightly reformatted from the TeX output):
Squeak Nile (Squeak-Nile)/Squeak
Number of Classes And Traits 3 6 -100% Number of Classes 3 1 66% Number of Methods 39 33 15% [*1] Number of Bytes 1328 1078 18% Number of Cancelled Methods 0 0 0% Number of Reimplemented Methods 10 3 70% [*1] Number of Methods Impl. Too High 0 0 0%
[*1] This includes 2 subclassResponsibilities in Squeak which should be discounted as pointed out above. The main differences are in the number of entities as well as in the number of methods (overrides). Looking at it in detail it turns out that the larger number of entities comes purely from the more fine-grained structure of traits (only one class but five traits) and the larger number of methods come from overrides where InternalStream has either more efficient versions (#upTo: #next: #upToEnd) or needs to compensate PositionableStream assuming that the position will be within its readLimit (#position: #setToEnd #reset) or implements required subclassResponsibilities (#atEnd, #contents).
In the paper, I compared the existing design of Squeak and Nile. In my opinion it was fair in this respect.
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. You could also have reimplemented Stream and PositionableStream in InternalStream and have better metrics :-). 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.
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?
One could rewrite the Squeak collection hierarchy to do without these overrides by relaxing the constraints on PositionableStream and use more effective versions by default. This would improve these metrics but I'm not sure it is in the spirit of the Squeak collection hierarchy.
The Squeak collection hierarchy needs to be changed also :-D. You probably won't agree with a new design based on traits ;-).
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?
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.