Traits vs. Interfaces (was Re: election details *PLEASE READ*)

J J azreal1977 at hotmail.com
Sat Feb 24 12:57:32 UTC 2007


>From: Andreas Raab <andreas.raab at gmx.de>
>Reply-To: The general-purpose Squeak developers 
>list<squeak-dev at lists.squeakfoundation.org>
>To: The general-purpose Squeak developers 
>list<squeak-dev at lists.squeakfoundation.org>
>Subject: Re: Traits vs. Interfaces (was Re: election details *PLEASE READ*)
>Date: Sat, 24 Feb 2007 01:21:59 -0800
>
>>Does it have to be this way, or are you looking at a... less then optimal 
>>use of a workable technology.
>
>Of course it doesn't have to be that way. But one of the hardest things to 
>do is to be honest about how particular features of languages *actually* 
>work out. It's like MI in C++ - of course there are ways of using it 
>"right" it's just nobody seems to use it that way (although I will admit 
>that my experience is dated - after having burned my fingers several times 
>I never used MI again other than in the Java way, that is by using abstract 
>virtual base classes).

Well I agree.  When people use something badly we must always ask ourselves 
if it was because the feature itself is bad.  And we must be prepared to say 
yes.  Two examples would be... the entire C++ language and the entire Perl 
language.  It's always funny when people say "this is 
unreadable/unmaintainable!" and proponents  simply say "but you *can* write 
readable code with it!".

I don't want Squeak/Smalltalk to be laughed at in this way, but at this 
point I feel, as you say, we don't have overwhelming evidence yet as my two 
examples do.

>[... snip ...]
>>I hear what you are saying about "use a base class", but does my base 
>>class still have to say "implements"?
>
>That depends on your particular flavor of interfaces. BTW, I do see your 
>point about Haskell but note that in your example you have both given the 
>definition as well as the constraint on a particular interface - in my 
>understanding this is more like specifying type algebra or pre/post 
>condition and not as much about actually providing the code for the 
>implementation.

Well, I don't know what theories the Haskell community may try to place 
around what is happening, but I know what it actually does:  it's just code 
implementations of those functions.  If you wanted you could override both 
of those in your new type to just return False.  The only constraint that 
was put in place was the function type signature (takes two of type anything 
as arguments and returns a boolean).

In fact you can use type classes to actually do OO, complete with (compile 
time) function overrides based on type.  This I know from experience because 
I translated my Smalltalk Recurrence rules to Haskell directly, and it 
worked. :)

>That's why I mentioned delegation. You could have an object encapsulating 
>the drawing policy and have your three classes delegate to it. In which 
>case you not only get the interface but also the ability to dynamically 
>change the drawing policy - at the cost of (at least) one slot for the 
>object representing the policy. To me, this is a much cleaner way of 
>dealing with the problem you are describing while preserving all of the 
>good properties of objects.

Yes, this is one reason I don't personally do a great deal of inheritance.  
I find that other relationships are often more appropriate.

>>Is this a problem with the technology (Traits), or the implementation?
>
>There is not enough evidence to draw a conclusion yet. The evidence is that 
>this implementation seems to have a problem towards that end and the 
>implementation was strongly driven by the desire to eliminate code 
>duplication (more so than to structure it into logical aspects). I think 
>that the attempt to eliminate duplicate code at all costs using traits may 
>ultimately lead to this effect simply because if you try to squeeze all the 
>entropy out of the system then the result will be a static structure.

Yea, lack of tools are really hurting us here.  Does anyone have a diagram 
or something on what traits are in the image, dependencies, etc.?

>Interfaces on the other hand, precisely *because* of the overhead they 
>require tend to be more minimal, therefore more abstract and therefore more 
>easily to maintain and (re)implement.

But, as in my Eq, Ord example, this overhead may also ensure they are never 
used for one of things I think they are most applicable for.

>>Well that is a good technique.  I don't personally see Traits as hammer or 
>>a silver bullet, but I do think they (at least have potential to) fill a 
>>hole [1].
>
>Are we talking about a hole in Java or in Squeak?

I meant in OO in general.  Not to say OO is flawed, just that this is a 
piece that was not part of the concept in the beginning (since it was 
probably decided that inheritance can handle all the cases, which is true.  
It's just a question of organization) that probably needs to be.  And I feel 
safe saying this given the fact that every (most?) language that supports OO 
has some way of doing it.

>Your example only applies to Java, not to Squeak (which has had #=, #==, 
>#~~ and #~= in Object from its very first day). For that particular problem 
>we didn't need traits.

I would disagree here.  Well, not that you don't *need* traits.  Obviously 
Smalltalk wasn't crippled without them, but something that fits this role 
(at least the role I envision them filling) does make things clearer IMO.

For example, you mentioned that Squeak has #=, #== etc, and that is true.  
And testing if an object is the same instance as another (#==) does make 
sense for all objects.  But does #=?  Are all objects capable of being able 
to compared to other objects for equality?  What about #<, #>?

It is a powerful capability Squeak/Smalltalk has to ask an object if it 
implements a method.  But in the case where someone asks if an object 
implements #=, what they really want to ask is "are you an object that can 
be compared to another for equality?".  And that implies not only #=, but at 
least unequal as well.

I don't know exactly what Traits have been used for in the image, but I see 
them as the solution to the "protocol" question that comes up from time to 
time.  You can do the same thing with inheritance but you end up with either 
really odd trees (e.g. ObjectWithEquality, ObjectWithoutEquality, 
ObjectWithEqualityAndOrdering), or a lot of #shouldNotImplement.

>>But since Traits can't have variables, I would expect them to be useful in 
>>many of the same ways Haskell type classes are.  I could be wrong, but I 
>>hope I'm not and I can't think of a technical reason Traits must create 
>>fragility.
>
>I need to read more about Haskell's type classes - I never looked too 
>closely at them. Do they actually specify implementation or is it all type 
>algebra (like in your example before)?

My example before was showing the actual implementation.  What you may be 
thinking of is that often the default implementation isn't given, just the 
type signature.  This would have looked like:

class Eq a where
  (==) a -> a -> Boolean              -- takes any type, followed by another 
of that same type
  (!=) a -> a -> Boolean               -- and returns a Boolean.

In that case, you are just specifying what functions must be present, and 
giving their type signature.  What I showed above gave two default 
implementations (and left out the type signature to be deduced by the 
compiler).  That way, if you go against the tide an decide only to implement 
(!=) then == will work sensibly (since it just calls not on the results of 
your ==).

>>But I also wonder if it is sort of a "hot button" issue.  I mean, there 
>>are things to be frustrated/embarrassed about here (e.g. releasing 
>>something into the image that appears to have no means of maintaining).
>
>Of course it is frustrating. But what is worse is that while the paper 
>examples all look fine I just haven't been able to find an improvement in 
>understanding or design when I look at the (admittedly only) example of a 
>real-world use. If that is the future with traits then it is a bleak future 
>indeed. And of course it is quite possible that tools are missing, that 
>early explorations go wrong but at least we should be honest enough to 
>reflect critically on the artifact that was produced and see what is right 
>and see what is wrong.

Well, I think the tools are the problem here.  Either there are not any, or 
at least no one seems to know where they are (I know I don't, and I see the 
question come on the list just about anytime says "Traits").

And I suppose another problem is, Traits are kind of an advanced feature.  
The case of Eq, Ord and similar are very simple.  But what about Seaside?  
Should render classes all inherit from one, or are they simply unrelated 
classes that happen to have a specific trait (the ability to render) in 
common?

My take in this would be rendering classes inherit, but classes that get 
rendered may just have "renderable" trait since many of them (at least the 
ones I make) are complete domain objects that just happen to implement the 
#renderOn: message.

But even in this contrived example, we can see that there can be ambiguity 
in how they are used, and therefor they can be used wrong.  Whether this is 
a "this language is unreadable/unmaintainable!" level issue is something we 
will have to discover.

_________________________________________________________________
Play Flexicon: the crossword game that feeds your brain. PLAY now for FREE.  
  http://zone.msn.com/en/flexicon/default.htm?icid=flexicon_hmtagline




More information about the Squeak-dev mailing list