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

J J azreal1977 at hotmail.com
Sat Feb 24 08:23:06 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: election details *PLEASE READ*
>Date: Fri, 23 Feb 2007 22:35:35 -0800
>
>The main problem is the (mostly) static nature of interfaces in Java. E.g., 
>declaring them is a pain, otherwise I would disagree. If you want to share 
>a default implementation use a subclass - one of the big problems I see 
>with traits is that their interfaces get so specific that an *alternative* 
>implementation becomes effectively impossible (therefore completely loosing 
>the point about interfaces as abstractions).

Does it have to be this way, or are you looking at a... less then optimal 
use of a workable technology.

You asked before if I had used traits, and of course I haven't.  I haven't 
felt the need to reach for them so far, and I am under the impression that 
the tools aren't ready anyway.

But I have used Type classes in Haskell which is where I am taking my 
vantage point from.  For those who may not know, type classes just specify a 
specific "interface" (set of functions), and any type that wants to be in 
that type class must provide an implementation.  The class definition can 
specify default implementations.  For example:

class Eq where
  (==) lhs rhs = not (lhs != rhs)
  (!=) lhs rhs = not (lhs == rhs)

Now, as you notice, the definitions of those functions seem to depend on 
each other.  The compiler would detect this and make an error, of course, if 
someone tried to use them both.  It is simply a way to ensure that an 
instance must supply either one, but they don't have to supply both.

For me, this provides everything that Java interfaces do (including type 
safety.  You can say e.g. my new function works on any type, so long as it 
implements the Eq class) and more, since the default implementation avoids 
so much code duplication [1].

I hear what you are saying about "use a base class", but does my base class 
still have to say "implements"?

Either way, this feels to me like another manifestation of a problem I 
always had with Java:  implementation inheritance.  I may make 3 totally 
different window widgets, and by rights each one would "implement" a draw 
API, but I will have to invent another class for them all to inherit to 
avoid duplicating a bunch of unrelated code.  People reading my code may 
wonder why "3DGameWindow" is in the same tree as "TaskBarClockWidget", but 
it is just to save code duplication.

This is what I have hopes that Traits can help me avoid.  I only inherit 
when there is an inheritance relationship present, and Traits are for the 
code that is in common but only as a coincidence (e.g. a car and a plane can 
both drive on a street, but the only thing I see in common are the wheels).

Personally my inheritance trees never get very deep.  Normally just one 
level down.

>For example, look at TAccessingMethodDictDescription (which is simply the 
>first in the browser) - it is practically impossible to reimplement this 
>(or any other) of the current traits for the class kernel in any way that 
>doesn't exactly follow the current implementation (like if TAMD is 
>responsible for accessing the method dictionary then we should be able to 
>reimplement MD access without affecting any other trait, right?).

Is this a problem with the technology (Traits), or the implementation?

>As I see it, there are *way* too many dependencies between these traits and 
>interfaces can help to abstract from these dependencies. For sharing 
>(default or not) implementation, Java (as well as Squeak) has a single 
>inheritance tree which works for almost all purposes just fine. If you use 
>delegation instead of inheritance it works for *everything* just fine and, 
>IMO, typically results in a better set of collaborators because suddenly 
>these roles become objects themselves.

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].

>Early on I thought the same way (and this was why we started thinking down 
>that road when Nathanael interned with us). However, in practice it seems 
>like Traits are mostly being used as a thinly veiled version of MI with all 
>the same problems. You have correctly pointed out that Java interfaces are 
>"nothing but abstract virtual base classes in C++" - which coincidentally, 
>was the only way that MI ever worked in C++, which itself has two dozen 
>ways of doing MI wrong and exactly ONE to do it right (abstract virtual 
>base classes).

Well, C++ was my favorite language for a long time (before I met Smalltalk 
:), and I have used it quite a bit.  And I couldn't agree more.  The only 
useful purpose I ever saw MI serve was just what you said.

And this is one thing I am paying attention to with the Traits stuff.  There 
has been talk of adding variables to Traits and I (strongly) disagree with 
this idea.  At that point, IMO it Traits would just be brining in MI through 
the back door.  What I always think when I, or anyone else, came up with the 
need to do MI is "rethink the design".  In every case I have ever seen, some 
other pattern has always been a better choice.

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.

>Java choose a stand on that and it worked (which is pretty impressive given 
>that it was the first language that used interface to that extent). Traits 
>seem to reintroduce a number of ways of using MI wrongly (at least that's 
>the feeling that I get when I look at the practical example) and that's why 
>I prefer interfaces.

I understand your concerns.  I never want to see Smalltalk go down the MI 
road either.  But for me, the presence of variables is what differentiates 
between MI and "interfaces with default implementations".

My point of view comes from seeing a system that has this and seems to work 
very well.  I don't know what your experiences are, but if it's limited to 
C++ then I don't wonder at your skepticism. :)

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).

[1]  Imagine the situation you would be in in Java if all class that can be 
compared for equality must implement this interface!  Every single class 
that implemented the interface would have one of those two functions just be 
a "not" of the other.  And I don't think your solution of making a base 
class is going to work here because we may also want classes like Ord (less 
than, greater than) and so on.  You would need a base class for every 
possible combination.

Of course the solution is to not have such a class, but is this the right 
solution?  Shouldn't we have some fine grained reflective way of knowing if 
the class supports a specific protocol like equality?

_________________________________________________________________
Refi Now: Rates near 39yr lows!  $430,000 Mortgage for $1,399/mo - Calculate 
new payment 
http://www.lowermybills.com/lre/index.jsp?sourceid=lmb-9632-17727&moid=7581




More information about the Squeak-dev mailing list