> > Well, what *else* could it mean? This is why I > said > > that the contract is in English, not just the > method > > signatures. > > Well, you didn't mention English. But yes, it > couldn't really mean anything else and still be > useful.
"Contracts are not expressible in anything less than a human language." I agree -- I didn't say "English". I presume one could define them in any sufficiently technically expressive human language.
> OK -- this sounds like something Java could pretty > easily do if every class had a corresponding > interface, because then the class that wanted to be a > "Point" would simply implement the Point interface > and delegate (or act as the "adaptor" as you > describe) to the other object, translating, e.g., > parameters (y, x) into (x, y). Is that a correct > statement?
I think Java could pretty easily do what you suggest. I've even heard it suggested, sometimes by me. (I first heard the idea from Guy Steele, who suggested it be legal to say "class foo implements Point", even if Point is a class.
But that's not what I'm saying. The problem with this is that the class designer must do this. The Haskell mechanism comes much later. Any programmer can, at any time, assert the mapability. This means that as a user of both packages A and B you can work around some problems by asserting the mapability of A.Point to B.Point.
Now to be fair I haven't lived with this feature because I've never programmed in Haskell (I've only read about it). But it seems fundamentally sound, and probably very useful.
> When you say "this cements it in", I'm not sure what > you mean. The cementing that I dislike is that of a > subclass to it's superclass parent; surely that > relationship is as cemented as can be.
What I mean is this. Let's say I want to create a fancy vibrating version of JButton. JButton has about a million or so methods. If I subclass JButton for this purpose, I only add the little bit of code to make it vibrate when it wants to be pressed. When the Java 1.6 API is released and another 500,000 methods are added to JButton, my code doesn't need to change at all (except for the rare corner case where they defined a new method that clashes with one of mine).
If I used composition on this problem, I would create a button that contains a JButton; then I would have to write a million pass-through methods. Then, for each release of the Java API, I'd have to create a modified version of my button class to support the different interfaces of JButton.
> Composition is by nature a looser coupling. One > object is dependent only on the interface of another. > I can plug-in a different object that will suffice as > long as it satisfies the same interface; such is not > the case with a parent-child relationship. > Composition also lends itself to a much richer set of > patterns that subclassing.
I don't think composition necessarily achieves a looser coupling. Seems to me, your class has to be tightly coupled to any objects that comprise it. If one of those contained classes becomes deprecated, or is not available, then you have to do a lot of work to disentagle your code from it. It also seems like you have to waste a lot of time writing code that is not related to solving the problem at hand, but merely to make the contained objects properly accessible (or just throw in the towel and make them public members).
In the case where the interface to the class is small, clean and simple, composition is great, but as Ken Arnold mentioned in the interview here, that is unfortunately often not the case in the real world.
> "Contracts are not expressible in anything less than > a human language." I agree -- I didn't say > "English". I presume one could define them in any > sufficiently technically expressive human language.
Gotcha - I was looking only at this current thread, not at an interview.
> I think Java could pretty easily do what you suggest. > I've even heard it suggested, sometimes by me. (I > first heard the idea from Guy Steele, who suggested > it be legal to say "class foo implements Point", > even if Point is a class. > > But that's not what I'm saying. The problem with > this is that the class designer must do this. The > Haskell mechanism comes much later. Any programmer > can, at any time, assert the mapability. This means > that as a user of both packages A and B you can work > around some problems by asserting the mapability of > A.Point to B.Point.
I don't see the point of this complicated sounding "mapper" construct when being able to say "Class X implements Y" where Y is a class will do the job well.
Say I'm a third party to A.Point and B.Point. I want to create a class than can take A.Point and make it masquerade as a B.Point so I can use some API that deals with B.Points.
All I need to do is create a class, MyPoint, that "implements B.Point" as we discussed, and takes an A.Point instance, and delegates/translates the methods of the B.Point interface to the A.Point instance.
Thus the suggestion that you and Guy Steele have thought of seems like the most intuitive "mapping" construct to me. It seems to fit nicely within the existing scheme of the language. (Granted, I haven't thought it through at all.)
If what Haskell provides is a mapping mechanism merely so that I don't have to create classes to get from A.Point to B.Point, then I'd simply wonder what the value is in not creating classes.
]> I don't see the point of this complicated sounding > "mapper" construct when being able to say "Class X > implements Y" where Y is a class will do the job > well.
You're talking compile time for the builder of class X. I'm talking compile time for the *user* of classes X and Y. If you write a perfectly good X class, and Jill writes a perfectly good Y class, and *I* -- a user of both -- would like to be able to treat X as a type of Y, then "implements" won't cut it.
> All I need to do is create a class, MyPoint, that > "implements B.Point" as we discussed, and takes an > A.Point instance, and delegates/translates the > methods of the B.Point interface to the A.Point > instance.
Given:
A.Point getCoords(); void printCoords(B.point);
Now, with a MyPoint model:
printCoords(new MyPoint(getCoords()));
But with the Haskell model:
printCoords(getCoords());
With the MyPoint technique, interaction between the two kinds of points must be mediated by the creation of an adaptor object. A model that told the runtime that the two were interchangable would allow all code using A.Point to use B.Point's with the same ease as if the originator of B.Point had subclassed A.Point.
Surely that's a non-trivial distinction. If the subtyping, or IsA, concept has power -- and the whole OO notion fundamentally assumes that it does -- then clearly it is more powerful to establish correct IsA relationships. Adaptors aren't IsA -- they're CanBeMadeToActAsA. And they have the resulting conceptual (and programming) weight. Would you rather create a new MyPoint object for every B.Point object, or provide one set of methods that allow every B.Point to be directly used as an A.Point?
> I think it would be interesting > to see a language that didn't actually have implementation > inheritance, just interface inheritance. It would also be > interesting to see a language, perhaps the same one, that > does away with publicly accessible constructors in favor > of static factory methods.
> A framework builder needs to be very concerned about > designing for subclassing, of course, while an application > builder *much* less so. I would suggest that an > application builder should keep classes final unless > there is a compelling reason to do so, since it > simplifies the model, and avoids the pitfalls admirably > illustrated by Bloch. Changing this decision later on is > just another refactoring, which does not affect the > consumer of the application in any way...
As an application programmer I tend to agree with this: we have to think much less about it, and we can refactor later. But I dont agree with the willy-nilly use of final. Firstly: a good application programmer tries to program every part of an application as a reusable framework.
Secondly: By using final you are saying to all would-be subclassers: "You cannot subclass this because it would be truly evil and I just can't allow it." Here you are assuming that there are would-be subclassers. If noone was ever going to subclass your class there's no point in making it or its methods final. And if someone does come to subclass your class, then finding final in the signature is going to scare them. "Why is it final?" "Is something bad going to happen if I refactor it and subclass it?".
Regarding the "addAll calls add and should therefore declare it in its documentation" problem.
I haven't read the full description of this problem from the original source - just the quote in this interview - so I could well be missing something but...
I don't see the need for documentation here. I have two takes on this in fact.
The first is: Any class that has a method addAll and another called add, almost certainly has a getCount() or size() or length() method that would obviate the need for doing so in add and addAll. And to those of you who would argue: "Use your imagination bozo this is just an analogy for a real case" I would reply: Don't bother me with the problem unless you can come up with a real world, at least sligthly probable example. Often these abstractions and analogies make the problem easier to understand, but they also tend to make us worry about problems that don't exist.
The second take, is assuming that there is a real world use for the add/addAll issue, perhaps the best policy is just never to do that. Just do