Thinking in Generics

Discussions

News: Thinking in Generics

  1. Thinking in Generics (59 messages)

    Bruce Eckel, author of Thinking in Java, was just at the Silicon Valley Patterns Group, and the topic that he choose was Generics. As the group went over the implementation of Java Generics, they ran into a few problems.

    He discusses other languages that have implemented this "Generic" feature, and how they differ.

    Read Bruce's thoughts on Generics

    Threaded Messages (59)

  2. I do not like generics but...[ Go to top ]

    ...Bruces comments are a bit short sighted.

    Generics in Java cannot be compared to generics in C++, because the compiler has no freaking way to check at compile time if the method actually exists, because there is no indication of the type of the argument anywhere. So to make Bruce's example work, you need to do away with compile time checking in generics.

    If you do not want to do that, then what they did is probably the best you can do. Generics in Java is more of a parametrization mechanism than a templating mechanism.

    Oh, btw. Bruce mentioned he likes the enum construct...well, I think since it is not type safe (multiple class loaders in server environment), you could as well do without it :-).
  3. Funny...[ Go to top ]

    Bruce has updated his blog with a response to comments on the original entry - and it's even more of a hash than the original.

    This is particularly true of his talking about methods vs. interfaces, and the Gun/Camera example. Rather incredibly, he completely misses the fact that multiple interfaces can have the same method name. :-) And that interfaces aren't just about encapsulating valid methods, but also of indicating what you expect an object to do. The choices he gives for the example are:

    - A Shootable interface w/ shoot() method
    - Use the class, not an interface

    and that's all. He goes on to talk about how interfaces can't convey semantics. This is of course true - but what he misses is that interfaces _can_ convey the developer's intent.

    In the case at hand, the problem is that he's looking at interfaces only as actions (well, really adjectives which describe use) - like Shootable, Serializable, etc. This is valid for some domains, but isn't IMO the only use of interfaces. Often, the interface is more like a noun - a BinaryLog, a JMS Message, a Command object etc. Indeed, it's interesting to consider the thought processes behind a Command object and, say, an Invokable object (which we'll assume are two interfaces both with the same method name).

    To the case at hand - would the imaginary high level code really be dealing with a Shootable? Or would it perhaps be dealing with a RangedWeapon:

    public interface RangedWeapon {
    void shoot();
    }

    and an Imager:

    public interface Imager {
    void shoot();
    }

    Both have a "shoot()" method - but it's clear that there's a difference between a RangedWeapon and an Imager (and it's clear you couldn't pass a RangedWeapon to an Imager method, or vice-versa).

    Perhaps because he doesn't see the "other side" of interfaces (the Imager/RangedWeapon side vs. Shooter) he doesn't get how people might do something like <T extends RangedWeapon> instead of <T extends Shooter>.

    Or maybe he just finds it excruciatingly difficult to create interfaces and hates typing? :-)

    -Mike
  4. Thinking in Java 4th Edition[ Go to top ]

    Better that Bruce think about these things now before they make it into his 4th edition of his "Thinking in Java" book. Bruce has been busy of late working on his book "Thinking in C++, Vol. 2: Practical Programming, Second Edition" so he's no doubt in a C++ mindset and NOT been keeping up on JDK 1.5 until recently.
  5. re: shooting in the foot[ Go to top ]

    I think creating contrived examples hides the problem. Take a look the STL in C++. Agreed, the syntax of C++ templates is ridiculous. But, look at the power of being able to create generic functions. Apparently, you cannot do this in Java Generics.

    As an aside, this whole thing reminds me of the early arguments of interface vs multiple inheritance. C++'s multiple inheritance is extremely powerful - much more powerful than Java interfaces. But that power comes with sticky baggage that may not be worth the power.
  6. re: shooting in the foot[ Go to top ]

    I think creating contrived examples hides the problem. Take a look the STL in C++. Agreed, the syntax of C++ templates is ridiculous. But, look at the power of being able to create generic functions. Apparently, you cannot do this in Java Generics.

    IMHO the problem is not what you can do in a language - in self based languages you do very different things than in say Java then in C++ than in Smalltalk. It is more in not understanding that certain constraints that you *want* to enforce - like (sigh) type safety and early/late/very late binding - have certain consequences.

    As an aside, this whole thing reminds me of the early arguments of interface vs multiple inheritance. C++'s multiple inheritance is extremely powerful - much more powerful than Java interfaces. But that power comes with sticky baggage that may not be worth the power.

    It does not only come with baggage - it comes with conceptional problems that are very hard to be generically resolved (and I am quite sure they cannot be resolved). Much the same is true for Generics/Templating when compared with inheritance btw. For me breaking fundamental things like "what is inheritance" would have been enough reason to do away with generics altogether. But that's just me...
  7. Generics or Typed Collections[ Go to top ]

    I wouldn't discuss the theory about Generics, but one think I know:

    Using Generics in definition of classes of Collection Framework will simplify the code, eliminate the use of typecasting in recovery and (Great!!!) generate compiler error when misused.

    For me, this is the great advantage of the "Generics".

    If the Sun call this "Generics", "Typed Collections" or "Implicit Inheritance", doesn't matter for me.

    I use the advantages for my pleasure, and I will make some critics only when have one better sollution.

    Thanks

    José Augusto
  8. Misunderstandings in Generics[ Go to top ]

    "I was invited to be the guest at the Silicon Valley Patterns Group last night, and was able to choose the topic."

    Bruce may write wonderful books, but on several occasions now I have noticed that he speaks about things that he clearly does not understand _in depth_. This article is an excellent example.
    He would have never made a lot of the comments in the article had he read a bit about heterogenous vs. homogenous translation (you don't even have to be in San Francisco to understand that), the advantages and disadvantages of each approach, why the homogenous one was chosed for Java, and what that means.
    But I guess an understanding of the topic you discuss in front of an audience is too much too ask for these days.
    *sigh*
  9. Misunderstandings in Generics[ Go to top ]

    I totally agree with Bruce Eckel.
    Generics in C++ is one of the multi paradigm mechanisms of the language which is "quite" powerful.It allows a strongly typed language C++ to have dynamic-type-look-alike types.In addition it is a powerful code generation allowing developer to easily implement policy-based, parameterized designs.
  10. Misunderstandings in Generics[ Go to top ]

    And it allows C++ to have libraries that use generics without having to publish the library code?
  11. Misunderstandings in Generics[ Go to top ]

    I think you're understating the level of misunderstanding, if anything. Bruce manages to get it wrong in 6 languages. By his reckoning, he seems to expect Python, Ruby, Java, C++, C# and Ada to give him the same generics functionality. Too bad they're all wildly different. The C++ explanations are just plain wrong - he should read up on the role partial specialization and the linker plays in the bizzarro world of C++ templates (C++ templates are much later binding then he seems to realize). Based on his Python explanation (associated with Ruby), apparently he would consider Smalltalk message passing to also be semantically equivalent to C++ templates - hey, you don't need a specific type, so it must all be a flavor of generics!

    He's basically mangling several seperate concepts - dynamic languages vs. static, early vs. "medium" vs. very late binding, rather vanilla generic typing vs. open-ended turing-complete generics like C++ templates.

    Poor Bruce, he concludes: "Otherwise, one of the biggest potential steps forward in Java for a long time has just been royally flubbed, and I feel lied to (by the term generic)".

    Personally I'm luke-warm about generics in Java 1.5, but at least I took the time to understand them - and to specifically understand the tradeoffs backwards compatibility imposed on any solution. But it's kinda sad to see a celebrated technical author show a complete misunderstanding of very basic language design concepts.

    -Mike
  12. I agree[ Go to top ]

    I came to pretty much the same conclusion. What good is it if I have to know the type? I mya want to write a templatized class or method that works on classes that I did not write, hnce, have no control over the interface.

    And it does not work on native types either.

    C++ allows this: int i(2); which allows templatized classes/methods to work with native types as if they were classes with a default ctor.

    C# makes the same mistake.

    One argument I see frquently is the the idea of code bloat. I only saw this as a real problem once in the many C++ apps in which I was invovled. And that was a handheld application.
  13. I agree[ Go to top ]

    Sartoris - I don't know any competent language designer who would use C++ templates as the basis for a modern generics system. In C++ land they were an improvement, but outside of C++ land they look like (and are) a nightmare. The fundamental problem with C++ templates is the same one that plagues C++ in every other design area: an obsessive low-level performance mindset that overrides every other consideration.

    -Mike
  14. I agree[ Go to top ]

    "I don't know any competent language designer who would use C++ templates as the basis for a modern generics system"

    Do you know any language designers?


    I would argue that in most cases C++ looks less nightmarish than Java's "implementation".

    <T extends MyObject> is looks better than <T>? And is more understandable?


    ": an obsessive low-level performance mindset that overrides every other consideration. "

    How is Java's implementation less low-level than C++'s? I don't get that.
  15. I agree[ Go to top ]

    \Sartoris\
    I would argue that in most cases C++ looks less nightmarish than Java's "implementation".

    <T extends MyObject> is looks better than <T>? And is more understandable?
    \Sartoris\

    Syntactically, both suck big time. Java sucks a bit more because it doesn't have typedefs. But I wasn't talking about the syntax, I was talking about the semantics and how it works in reality for a developer using it day to day. Use a template wrong in C++ and you'll know _exactly_ what I mean.

    \Sartoris\
    How is Java's implementation less low-level than C++'s? I don't get that.
    \Sartoris\

    C++'s design is targetted towards code generation - generally the compiler/linker in concert are spitting out instantiated templates. The idea is that nothing should ever be done at runtime, only at compile/link time. It's a rather low-level concept because it rather explicitly assumes how the compiler and linker (and headers - oh yes, lovely text headers!) work and are used. Modern C++ systems try to optimize alot of generated code out when possible, but the fact remains that the original design is rooted very deeply in the C++-specific header/compiler/linker philosophy that dates back to Unix in the '70s.

    -Mike
  16. I agree[ Go to top ]

    <T extends MyObject> is looks better than <T>? And is more understandable?

    It does not look better, but it has a different meaning. The first one allows to use T whereever MyObject is required, the second does not.

    BTW: I've put togehter a simple quiz about the generics. http://www.grayman.de/quiz/java-generics-en.quiz
  17. Gregor's Generics Quiz[ Go to top ]

    Hey Gregor,

    I'm having fun asnswering your Quiz on Generics. I think Question 5 has two typos:

    1. s5.getElement(); should be b5.getElement();

    2. Answer C should referenece Source C not Source B.

    Cheers, Robin.
  18. Thanks[ Go to top ]

    Hi Robin,

    thanks. I will correct the quiz tonigt. :-)
  19. Thanks[ Go to top ]

    Please check your answer for Question 12 also - I find it surprising.
  20. Thanks[ Go to top ]

    It indeed is surprising. But the answer is correct.

    It is possible to put an apple into an orange basket, if the non-generic syntax is used. And that is why the compiler warns us that our code is not type safe. The type checks however are still in place in the JVM and so when we try to get an orange from the basket and get an apple instead, a ClassCastException will be thrown.
  21. Thinking in Generics[ Go to top ]

    Hm, am I surprised by the level of anger in this piece...
    I personally lost much of respect to Eckel after he flip-flopped on checked exceptions issue which is for me is kind of Litmus test on whether or not one understands modern software engineering.
    This article is nothing more than one-sided, unsubstantiated and biased rant. That’s all.

    Regards,
    Nikita.
    xTier - Service Oriented Middleware
  22. another claim made in his weblog[ Go to top ]

    "Ant sucks on a number of levels (so much that the original inventor became disgusted with it and went off to create something better.."
  23. Thinking in Generics[ Go to top ]

    Well, according to http://mindview.net/WebLog/log-0051 the quintessential truth about mind set such as Eckel’s is that basically we should loosen as much as constraints in a programming language as possible because in the end the "bad" programmer will always find another way to "mess up" while "good" programmer who always writes a bug-free and design-clean code will only benefit from constraints-less flexibility. If you share this dogma than by the same token I can see why checked exceptions are evil, interfaces have no semantic and C++ templates are the best generics design.

    I don’t know where to begin...

    Nikita.
    xTier - Service Oriented Middleware
  24. Write-only code[ Go to top ]

    If you read alot of his recent works, what he's really expounding is write-only code. He wants a language where it's as effortless as possible to write code - indeed where you can very much say code is organically grown as the programmer "discovers" the problem space. In this mind set there isn't much in the way of design or APIs, there are only amorphous Objects and method calls. Anything that gets in the way of this organic growth is considered bad.

    Sound alot like Smalltalk? At a high-level, that's what he wants - an extraordinarly write-friendly language.

    The problem, of course, is that it's extraordinarily difficult to divine the developer's intent in this sort of style. To understand the abstractions and how classes and objects interconnect, you have to sit in the environment for a very long time and run the code and inspect results (again, very Smalltalkian). You have to fully immerse yourself to have a clue to what's going on.

    In contrast, Java is an extremely read-friendly language. There is extra typing of course (in the combinatino of dynamic and static typing sense, and in the tap-tap-tap of the keys sense), which makes it not as fun to write, but it shines in people being able to read the code, make sense of it, and change it.

    C++ is of course hostile to everyone - readers, writers, and your little dog, too. But the template mechanism at least doesn't do typing in the way Java people understand it - instead, matches really are done at the method signature level, not the class/type level. Which means Bruce doesn't have to tap-tap-tap on the keys to create an extra class (hopefully an abstract one which mimics Java interfaces).

    In some people the write-only dynamic not only makes sense, but it's the right thing. But in this particular case I think it's much closer to laziness, and not particularly caring how easy it is to read something after the fact. Indeed "not particularly caring" may the operative phrase here. Such people can write an astounding amount of code that works well too, but they're generally hell on teams.

    -Mike
  25. Smalltalk is not write only ...[ Go to top ]

    by any stretch of the imagination. Ask anyone who ever programmed in Smalltalk and they will tell you that Smalltalk is much easier to read then most languages. I spent 3 years programming in Smalltalk and it was easier to maintain the code then it is to maintain Java. For example in Smalltalk you have named parameters in Java you have to try to remember which comes first (for example GridLayout is the x size first or the y size first) without any clues. There are numerous examples like this. After programming in both Smalltalk and Java I definately agree with Bruce that static typing is too restrictive. You need to write a lot of code just for the compiler. As Paul Graham put it
    "A programming language is for thinking of programs, not for expressing programs you've already thought of. It should be a pencil, not a pen. Static typing would be a fine idea if people actually did write programs the way they taught me to in college. But that's not how any of the hackers I know write programs. We need a language that lets us scribble and smudge and smear, not a language where you have to sit with a teacup of types balanced on your knee and make polite conversation with a strict old aunt of a compiler."
  26. Write-only code[ Go to top ]

    "In contrast, Java is an extremely read-friendly language. There is extra typing of course (in the combinatino of dynamic and static typing sense, and in the tap-tap-tap of the keys sense), which makes it not as fun to write, but it shines in people being able to read the code, make sense of it, and change it.
    "
    In general, the more tap-tapping being done the more bugs that exist, the more execution paths that must be tested, etc. Maybe for you it's a trade worth making. I tend to lean the other way...
  27. Write-only code[ Go to top ]

    In general, the more tap-tapping being done the more bugs that exist, the more execution paths that must be tested, etc. Maybe for you it's a trade worth making. I tend to lean the other way...
    The short response to that is bullshit.
    The medium-sized response is: look at Forth, perl, APL. Those languages all have few tap-tap-tapping compared to Java.
    Supersized response: 5 lines of perl can, in the real world, exhibit more bugs than 100 lines of Java. Denser code with lots of implicitness going on (think: implicit stack, implicit $_ args, etc) has a tendency to magnify bugs, not shrink them.
    -Mike
  28. Thinking in Generics[ Go to top ]

    Well, according to http://mindview.net/WebLog/log-0051 the quintessential truth about mind set such as Eckel’s is that basically we should loosen as much as constraints in a programming language as possible because in the end the "bad" programmer will always find another way to "mess up" while "good" programmer who always writes a bug-free and design-clean code will only benefit from constraints-less flexibility. If you share this dogma than by the same token I can see why checked exceptions are evil, interfaces have no semantic and C++ templates are the best generics design.

    I don’t know where to begin...

    You could begin by not mis-represent Eckel.

    <strong>He does not claim that you should have no constraints.</strong> He points out that loosening (as opposed to dropping) constraints can be useful, giving as an example that what OO got us over functional programming was a looser way of dealing with type constraints - not by dropping strong typing but by doing it differently. He then argues that latent typing is another useful loosening of constraints, and that this latent typing is what gives you the power of generics. It's not a 'let the programmers do anything' approach: Latent typing IS typing and IS type-checked by the compiler, it's just not the sort of type-checking you're used to.

    So he thinks that latent typing is a way that you can rework the constraints only slightly, and in doing so enable programmers to express simply things that can otherwise only be expressed in complex ways.

    And his claim that interfaces have no semantics beyond naming methods is simply true: semantics is what the program DOES and interfaces don't DO anything except enforce that methods with certain signatures exist. Latent type checking does the same.

    A straw man is easier to attack than a real target. But if you want to argue with his points I think you should argue with what he actually said, not with a caricature of it.

    Sean
  29. Thinking in Generics[ Go to top ]

    He points out that loosening (as opposed to dropping) constraints can be useful, giving as an example that what OO got us over functional programming was a looser way of dealing with type constraints - not by dropping strong typing but by doing it differently. He then argues that latent typing is another useful loosening of constraints, and that this latent typing is what gives you the power of generics. It's not a 'let the programmers do anything' approach: Latent typing IS typing and IS type-checked by the compiler, it's just not the sort of type-checking you're used to.

    If you read Eckel's wider collected works, you can see that he likes as close to zero constraints as possible - he prefers "constraints=off;" if you will. He also confuses just about every possible language construct across many languages along the way. See his opening example salvo:

    Eckel:
    So for example, in Python you can do this:

    def speak(anything):
    anything.talk()

    Notice that there is no constraint on the type of anything, which is just an identifier. Except that it must be able to perform the operations that speak() asks of it, so that implies an interface, but you never have to explicitly write out that interface ? so it's latent.

    He goes onto say "In C++ you can do the equivalent". Which is plain wrong - he's confusing dynamic type checking with C++ template's deferred method signature matching. If you can read between the lines, you can see he prefers as little static typing as possible. The bullshit about interfaces "loosening" anything is just that - bullshit. Read between the lines and he doens't like the effort (mental and typing) of creating an interface that's meaningful. As a side effect he doesn't understand Java 1.5 generics at all.

    So he thinks that latent typing is a way that you can rework the constraints only slightly, and in doing so enable programmers to express simply things that can otherwise only be expressed in complex ways.

    All of this rests on the assumption: "can otherwise only be expressed in complex ways". Frankly, I think the assumption is false. The "otherwise" e.g. without so-called "latent" typing, is not complex to me at all. In fact, the assumption is laughable when you're implying that Java Generics require a solution to be "expressed in complex ways" when the point of comparison is C++ templates!!! C++ templates are a marvel of very smart technologist going wild with cleverness.

    And his claim that interfaces have no semantics beyond naming methods is simply true: semantics is what the program DOES and interfaces don't DO anything except enforce that methods with certain signatures exist. Latent type checking does the same.

    public interface Imager {
    public void shoot();
    };
    public interface RangedWeapon {
    public void shoot();
    };
    public class NerfGun implements RangedWeapon {
    ....
    };
    void doIt (Imager imager);
    ...
    doIt (new NerfGun());

    No semantics? Funny - it looks like the compiler will disallow the last line, even though NerfGun() has a shoot() method. It looks to me like the use of interfaces is further constraining the call. Isn't that semantics?

    A straw man is easier to attack than a real target. But if you want to argue with his points I think you should argue with what he actually said, not with a caricature of it.

    It's funny how often "straw man" comes up in these sorts of discussions. In the spirit in which you use it (and others), it seems to mean "how dare you think!!! You must not attempt to read between the lines, interpret, or otherwise engage your brain! Thinking is unfair!!!!". Isn't it rather dull living in such a literal world?

    -Mike
  30. semantics is what the program DOES and interfaces don't DO anything except enforce that methods with certain signatures exist.
    <P>
    At the risk of sounding pedantic, and just plain "being wrong", I'll throw in my two cents here. These forums are a great place to learn and I think the discussion here is as interesting as the article. So feel free to "school me", because I've always got more to learn.
    <P>
    Are semantics what the program does, or what the program indicates it will do?
    <P>
    From a dictionary: semantic (adj) : of or relating to the study of meaning and changes of meaning
    <P>
    Which brings us to the definition of "meaning": (1) To have in the mind, as a purpose, intention, etc.; to intend; to purpose; to design; as, what do you mean to do? (2) To signify; to indicate; to import; to denote.
    <P>
    Do interfaces signfy? Indicate? Import? Denote? I think so. Interfaces, properly used, can signify a lot more than just what method signatures an object supports. They can convey a categorization of purpose, without forcing a particular implementation. I definately see that as as semantics.
    <P>
    Even if one desires interfaces to convey a definition of implementation, and not just a definition of intension, static-crosscutting using Aspect Oriented tools like AspectJ can help. One can provide a default implementation for an interface and enforce contractual obligations in a modular way.
    <P>
    Now back to the subject of latent typing, I think it boils down to whether the interfaces are explicit or implicit. For each latent type, in Java you could just define an interface. Is that really so hard? I don't think so, and the benefits are clearer code, more easy-to-understand type enforcement at both compile and link time.
    <P>
    I'm looking forward to tools like AspectJ supporting generic types; I'm still getting my brain around what's to come.
  31. Thinking in Generics[ Go to top ]

    And his claim that interfaces have no semantics beyond naming methods is simply true: semantics is what the program DOES and interfaces don't DO anything except enforce that methods with certain signatures exist. Latent type checking does the same.

    What the program DOES is only observable when you actually run the program. Everything else, be it interfaces, method names and even procedural statements, will only give you hints as to what the program might do when it runs. Every program is contingent on some runtime dynamics that need not even be deterministic. The question is, how does one developer tell another developer in a productive way what he _intends_ a program to do? Interfaces, method names, variable types, documentation, etc try to tell a story of what you can expect if you run the program. If you say, it is useless to try to convey this information, then you might as well do away with method names and start numbering your methods because the name doesn't have any meaning anyway.

    This is clearly nonsense and I remember very well how it felt to read through masses of C++ header files to find out what a method really does with its type parameterised arguments. Mind you that basically _all_ the procedural code of a templated C++ method goes in the header file instead of the implementation as result of the perverse way C++ templates work. What you do when you read all this code is actually build a mental picture of the thing that Bruce Eckel calls a latent type. You look at each and every place where a parameter is used and you mentally write down the methods called on it (or the operators used on it).

    I'm simply too lazy to do that. I hate to read other peoples procedural code. I want the signature of a method to give me enough information to narrow down the things I have to try. I want to look at the implementations only if absolutely necessary and I don't want to throw lots of useless test code at a method just to find out the most basic preconditions for calling it.

    It may work in a situation where there is a single person who writes all the code, knows it very well and doesn't have to look at it again after a number of months. This is a situation you have as a trainer or a book author or as a sysadmin but not in any real world software engineering type of project I know.
  32. Thinking in Generics[ Go to top ]

    This is clearly nonsense and I remember very well how it felt to read through masses of C++ header files to find out what a method really does with its type parameterised arguments. Mind you that basically _all_ the procedural code of a templated C++ method goes in the header file instead of the implementation as result of the perverse way C++ templates work. What you do when you read all this code is actually build a mental picture of the thing that Bruce Eckel calls a latent type. You look at each and every place where a parameter is used and you mentally write down the methods called on it (or the operators used on it).

    Excellent post, Alexander. I might add that C++'s implicit type conversion can make the difficult job you've already outlined that much harder. The net effect is that often C++ developers have to mentally act like a C++ compiler - which is _not_ a fun job. C++ Templates alone are bad enough - coupled with the rest of the C++ features (purposely all intertwined by design) and now you've got yourself a challenge. A rather unnecessary challenge as other languages have shown.

    -Mike
  33. Thinking in Generics[ Go to top ]

    It sounds as though you've had some bad experience with C++. The problem with a lot of C++ code that exists is that it was written by former C developers who did many things the C way instead of the C++ way. C++ code can be just as easy to read as Java code, in some cases more readable. But that's just my lame opinion.

    A problem that Eckel is trying to explain (at least in my reading of it) is that forcing that template to know about particular interfaces is useless. And he's right for the most part. That's because you don't always have control over the interfaces.

    An real-life example that came up on a project I worked on a few years ago involved having to work with class types that were generated by an idl2c generator (for CORBA). The generated types were beyond my reach. I couldn't specify what types they were. The CORBA server (not the ORB) had to create multiple factories for these types. I had 2 choices, type a factory class for each type, which could have been many, or use a template to write 1 class that handles them all.
  34. Thinking in Generics[ Go to top ]

    I think Eckel missed the main argument against the "template implies an interface" paradigm in this article. What really worries me is not that a programmer might pass in the wrong type of argument to a generic method, as in the Camera vs. Gun example. Sure that's one problem, but not the big problem IMO.
    The big problem with the "template implies an interface" paradigm is that the interface is implied. Like legal contracts, I think interfaces should be made as explicit and accurate as possible to make sure both the template designer and the template users understand what is expected of them. Consider this generic function:

    public <T> void doSomething(T x) {
    x.add(1);
    }

    What exactly is the interface being implied here? Minimally T should support "void add(byte)" (maybe return something other than void). But isn't it possible that at some point the template might be reimplemented as:

    static final int ADDED_VALUE = 1;
    public <T> void doSomething(T x) {
    x.add(ADDED_VALUE);
    }

    Such a change would break existing code that only supports "void add(byte)". How do you know if the maker of the template only intends to add bytes or he might decide in some future version to add ints, longs, or even floats god forbid? Making the interface explicit forces the implementor to think hard and specify exactly what his requirements are and give the users of the template a clear specification of what is required of them. The implementor can still change the interface, but that has to be his explicit intent, it can't happen as a side effect of changing a minor implementation detail.

    An additional concern is that with the "implied interface" it very hard to figure out what functions you (the user) are supposed to support and what they should do. For this reason no competent template designer would design a template without documenting the required functions and their expected behavior - you can't expect your users to read the sources and figure it out for themselves. If you are going to write these requirements anyway, why not put them in an interface so users know where to find them and the compiler can understand them and enforce them (at least at the signature level)? In the example above, the compiler can check that you support "void add(int)" even if right now the template only needs "void add(byte)", so that future versions don't break existing code. Why is placing these requirements in an interface so much harder (on the template designer) than writing them in the documentation? If anything the interface spec is a lot more concise, not to mention accurate.

    As for the "no semantics at all are bound to an interface" claim, I have to say I was quite stunned by it. IMO even though an interface cannot *enforce* semantics, it certainly does specify semantic requirements. An implementation which does not follow the semantic requirements of an interface does compile, but it is not a valid implementation and is probably a result of a programming error. Just because the compiler can't catch it doesn't mean it's not an error.
    If you implement "equals" such that "a.equals(a)" returns false then you are violating the semantic contract of the "Object.equals(Object)" method and you must expect your code to fail spuriously whenever you pass "a" to code written by someone else. It certainly may cause collection classes to break down or go into infinite loops. Same goes for any other method. This point is so fundemental to the idea of design by contract that I really can't understand Eckel's take on this one.

    There is also a weird air of surprise in this article. "I feel lied to" really caught my eye. If Eckel feels lied to then that's nobody's fault but his own. There were various review drafts very similar to the current spec back in 2001. When the JSR was created in 1999 there was already a strong implementation (Generic Java, based on Pizza) that was listed as the basis of work (and is actually very similar to the final spec). Various aricles describing this implementation (as well the the NextGen extension and the MIT PolyJ system) appeared in POPL and OOPSLA of 97 and 98. So everybody had a great deal of time to learn these implementations and comment on them. The article makes it sound like the expert group "pulled a fast one". In reality, if anybody didn't make his comments or didn't learn the proposed spec, they can only blame themselves. If the expert group's interpretation of the term "generic" surprises Eckel then he must have been detached from all the Generic Java efforts in the past 7 years. This quote is particularly perplexing:

    "But don't implement something whose sole purpose is to solve the casting problem in containers, and then insist that on calling it ``Generics.'' "

    Perhaps the interpretation of the term "Generic Java" developed during 7 years of concerted efforts in the academic world, JavaSoft, the Java community and JSR#14's expert group is as appropriate and worthy as Eckel's few-weeks-old impression?

    Gal
  35. Maintainability[ Go to top ]

    I would hate to be the guy that would be maintaining one of Bruce's softwares. Only Exception thrown and caught everywhere, no meaninfull Interfaces to distinguish between a camera and a weapon. And now he even wants to get rid of the interfaces completely. That would be a complete nightmare.

    He can save a few hours of typing and the people that come in after will have to spend a few weeks to find out what is going on in the code.
  36. Thinking in Generics[ Go to top ]

    An additional concern is that with the "implied interface" it very hard to figure out what functions you (the user) are supposed to support and what they should do. For this reason no competent template designer would design a template without documenting the required functions and their expected behavior - you can't expect your users to read the sources and figure it out for themselves. If you are going to write these requirements anyway, why not put them in an interface so users know where to find them and the compiler can understand them and enforce them (at least at the signature level)? In the example above, the compiler can check that you support "void add(int)" even if right now the template only needs "void add(byte)", so that future versions don't break existing code. Why is placing these requirements in an interface so much harder (on the template designer) than writing them in the documentation? If anything the interface spec is a lot more concise, not to mention accurate.

    C++ is rather enamored with what I call "magic methods". Without a single rooted hierarchy, and without real interfaces, the C++ paradigm is that you just have to know about certain methods that don't come from anywhere - like all the operator() methods. Whereas a newbie Java developer can go look at the javadoc (or source :-) for Object, a C++ newbie has to take certain method names on faith (unless he wants to look at the source for a C++ compiler :-).

    Templates bring the magic method concept down to the user level. This is supposedly a good thing: "Now you, the lowly application developer, you too can require magic methods!". But as you said Gal, in the template situation now you've got a pretty fuzzy contract. This may not be too much of a big deal for highly stable templates, but it's a nightmare for on-going development of them on large projects. And once again, for on-going development the type-inference crap (and potential ambiguity rules) in conjunction with templates can whack you really, really hard.

    It still boils down to the same thing: Eckel doesn't like to tap-tap-tap on the keys. That's seems to be the only real argument he's got going. I think he's disappointed that Java Generics hasn't reduced his keyboard typing level as much as he thought.

    -Mike
  37. Bruce Eckel seems to equate Generics with function templates from C++ in his article. Looking at it that way, you can see how he might come to his conclusion that generics don't really give you anything above what interfaces provide. Fortunately that's not all there is to it: specialisation at the class level rather than the function level, which his article ignores, is where Java's generics start to look compelling. That's also where this idea of "latent types", which Bruce seems so fond of, runs into problems.

    I've worked on C++ code which used template classes and it was a nightmare to figure out what you could actually instantiate the template with. Java generics code is instantly clearer: the requirements for an instantiating class are stated explicitly, so you can see right away what will and won't work. It also means that the compiler can give a more meaningful error message when you get it wrong. Going beyond that, the interface can indicate what the intended semantics for the instantiating classes are (Bruce needs to separate semantics from implementations in his thinking).

    The Java approach to generics requires a little bit more work up-front than C++ does, but saves you a lot more work later on. I'm surprised that someone as widely known and respected as Bruce Eckel has failed to see that.
  38. Thinking in Generics[ Go to top ]

    In my oppinion the reason why Java and C# can't handle C++ style templates is the good old C/C++ header files.
    In C++ templates (that can only by the way be defined in the headers) are in some ways equivalent to what XSLT transformation does to XML.
    Seems that java and C# bytecode metadata is missing this type of information and this is why Java/C# pre-processors are unable to generate transient types when applying templates to the user code.
  39. Java's version of generics works within it's type system. Unlike the C++ linking process which provides the closure for all of the templates, Java requires type safty based on a single class. As such, the generics implementation is a good one given the engineering constraints that exist.

    That being said, all of the discussion is based on an environment where developers write their own new code. The major problem with requireing an interface is what to do when there is no such interface. Third party libraries and other people's code are going to be excluded from template use.

    This is not the fault of the generics implementation, it is a result of the dynamic class loading that Java is famous for.

    Bruce is correct, this makes the template featues in Java less usefull than C++. While I will miss the power, I would not give up web and J2EE containers and dynamic class loading for that extra power.
  40. That being said, all of the discussion is based on an environment where developers write their own new code. The major problem with requireing an interface is what to do when there is no such interface. Third party libraries and other people's code are going to be excluded from template use.

    This makes no sense and never really has.

    If you're talking about is a single library, then you likely don't need generics - just code to the classes/interfaces therein. If you do need generics within just that library, the library will certainly have intefaces/classes you can use to constrain your generics.

    If there are multiple libraries and you want to use a common generic over all of them, then you're just plain hosed. What you're postulating is that a 3rd party lib won't implement some interface, and yet it will just happen to magically have a method (or a number of methods) which does _exactly_ what your template wants, right down to the signature? And presumably the other 3rd party libs will also have exactly the same method(s) and the same semantics.

    In Java (and I'd propose in any really language that's scalable from a development perspective) when objects have a method or methods that taken together imply a given set of semantics, then you use an interface to specify this in a concrete manner.

    Differing 3rd party libraries just don't have a bunch of methods that happen to match and a template can hook into and expect to work consistently. If there is commonality, it's invariably expressed via an interface. If you're only targeting one library then that library's classes and interfaces are what you target for your constraints.

    Really - why would you target individual methods and assume that across different classes they all just happen to mean something? In fact, you've just fallen into the Camera/Gun quagmire.

    -Mike
  41. It makes perfect sense to me and always will :-)

    Let's talk about the "single" library issue. Let's say you have a third party PDF library. Class PDFDocument has a print() function. You also have your own HTMLDocument class that has a print() function. You will not be able to write a generic class that takes a type <T> and call the print function without doing the following:
    1.) create an interface.
    2.) create two bridge classes (one for PDFDocument, one for HTMLDocument).
    3.) write the template class that uses the interface created in (1).

    The point, and the only point I'm trying to make, is that once you go through these steps there is NO POINT is using a template. At this stage, simply code to the interface.

    Now let's talk about the "multiple" library point of view. Here you can't ensure that the signatures are the same so you still have to do all the steps outlined in the single library case. Generics never would have worked here without bridge code.

    As far as falling into the Camera/Gun quagmire. You are correct, I'm in that quagmire. The problem is that in order to support DigitalCamera/FilmCamera you open yourself up to the Camera/Gun stuff. When dealing with jar and class files you don't have the option of inserting an interface where you want one.


    Here is the axiom behind Bruce's problem with Java generics. There are two basic ways of dealing with complexity in software: abstraction and indirection. Most software patterns are specific implementations of one or both of those axioms. In C++, you can use indirection with templates without having abstration. In Java, you can use indirection with templates only with a defined abstration. This limits your options.

    You can argue both sides of the limited/safe vs. unlimited/unsafe. That's a good discussion to have with any language implementation.

    The first point is that you have just as much work with Java's version of generics as you had before. The second point is that you will not be able to use Java generics well unless you have control over all of the classes.

    John
  42. <john>
    Let's talk about the "single" library issue.
    </john>

    That is a good idea, I just wish you followed it and gave a *specific* example. I think the type of scenario you are describing in the post is extremely rare.

    <john>
    Let's say you have a third party PDF library. Class PDFDocument has a print() function. You also have your own HTMLDocument class that has a print() function.
    <john>

    Now, just having a "print()" function doesn't seem that likely. What printer does it print to? What type of exceptions will it throw in case of failure? What return value, if any, does it have (maybe request status or job queue ID?). Is it likely that all of those will be so similar that the same code is correct for both of them? Return values just happen to have the same types and mean the same things, the exceptions just happen to be the same, the parameters just happen match perfectly (in terms of their types, meaning, order)?

    <john>
    You will not be able to write a generic class that takes a type <T> and call the print function without doing the following:
    1.) create an interface.
    2.) create two bridge classes (one for PDFDocument, one for HTMLDocument).
    3.) write the template class that uses the interface created in (1).
    </john>

    Indeed you won't. But is it really that important? Do you have a lot of cases you can list where two libraries with no shared interface just happen to support the *exact* same methods, with identical semantic behaviors? I doubt it. Besides, is it really that difficult to handle this case with normal polymorphism using a wrapper or some similar pattern?
    I'm not saying that what you describe can't theoretically happen, but in practice I don't think it happens enogth to be a concern. I would say that even if it does happen there is a certain flaw in the template-based approach you describe. When a class implements an interface it's designer is declaring that he will provide behaviors that match a certain set of semantic assumptions dictated by the interface. You as a developer can develop generic code that uses the interface, being sure that your code will "make sense" on all implementations. When there is no interface involved, the semantic behaviors just *happen* to be the same. In the next version they might not be the same. As soon as the behaviors or even the signatures diverge your entire template approach breaks down completely (for instance if in the next version of your code you want to use "cancel()" in addition to "print()", but one library calls it "cancelJob()" and the other calls it "cancelPrint()"). I think you should try to produce designs that are a little more stable and depend less on coincidences than that.

    <john>
    The point, and the only point I'm trying to make, is that once you go through these steps there is NO POINT is using a template. At this stage, simply code to the interface.
    </john>

    I agree. Java Generics are not meant to solve this problem.

    <john>
    Now let's talk about the "multiple" library point of view. Here you can't ensure that the signatures are the same so you still have to do all the steps outlined in the single library case. Generics never would have worked here without bridge code.
    </john>

    Generics are not meant to work in this case, and as you pointed out once you write the bridge code you don't even need a generic type.

    <john>
    As far as falling into the Camera/Gun quagmire. You are correct, I'm in that quagmire. The problem is that in order to support DigitalCamera/FilmCamera you open yourself up to the Camera/Gun stuff.
    <john>

    Why is that? You can write generic code that uses the Camera baseclass.

    <john>
    When dealing with jar and class files you don't have the option of inserting an interface where you want one.
    </john>

    The question is when would you want to do that (when do the methods match exactly and you just wish both classes added "implements XXX")? As I've said, my guess is "rarely". And even if I could add an interface (which is possible with custom ClassLoaders) I don't think I would, for the reasons I outlined above.


    <john>
    Here is the axiom behind Bruce's problem with Java generics. There are two basic ways of dealing with complexity in software: abstraction and indirection. Most software patterns are specific implementations of one or both of those axioms. In C++, you can use indirection with templates without having abstration. In Java, you can use indirection with templates only with a defined abstration. This limits your options.

    You can argue both sides of the limited/safe vs. unlimited/unsafe. That's a good discussion to have with any language implementation.
    </john>

    I agree that it is a good discussion. I think the matter has been heavily discussed the past 6-7 years or so (in the Java case) and I believe the final result is definately preferrable to what Eckel is describing.

    <john>
    The first point is that you have just as much work with Java's version of generics as you had before.
    </john>

    That's not true. You will have less work in typing because your types can be generic. Generic types won't open up new ways to implement existing problems like in C++, because they are not Macro-expansion-style templates (a Turing-complete macro system, someone keeps pointing out) as in C++. They are just an extension to the type system, and they will save you work involved with typing: less casting, more readable code, less runtime errors.

    <john>
    The second point is that you will not be able to use Java generics well unless you have control over all of the classes.
    </john>

    That depends on what you mean by "use Java generics". You certainly can create Lists and Maps that contains classes that you do not control. You can also create generic code to handle classes that you do not control, but that are properly designed with subtyping relations. What you won't be able to do is take two classes that are completely independent and don't have any shared type and write code that handles them both correctly. I doubt any solution (including C++ template) will help you do that in more than a handful of trivial cases.

    Regards
    Gal
  43. My *specific* example in the one library case is the TIBCO libraries for receiving and posting async messages.

    When I develop my parallel library for sending any posting messages (to and from standard out in this case) for testing I would love to be able to include it with a templated class. But I can't. And I still will not be able to with generics with any less work than today.

    My point is not that Java generics are bad. They save typing and make collections type safe. I'll use them and be glad they are there.

    But other than collections, what will you use generics for? I've used C++ templates for a whole bunch more than that. That's my only point.
  44. <john>
    My *specific* example in the one library case is the TIBCO libraries for receiving and posting async messages.

    When I develop my parallel library for sending any posting messages (to and from standard out in this case) for testing I would love to be able to include it with a templated class. But I can't. And I still will not be able to with generics with any less work than today.
    </john>

    I'm not sure I understand your example. First of all, don't the TIBCO libraries support some standard interface such as JMS?
    I can't figure out this whole sentence: "When I develop my parallel library for sending any posting messages (to and from standard out in this case) for testing I would love to be able to include it with a templated class".

    <john>
    My point is not that Java generics are bad. They save typing and make collections type safe. I'll use them and be glad they are there.

    But other than collections, what will you use generics for? I've used C++ templates for a whole bunch more than that. That's my only point.
    </john>

    You can use them to implement complex numbers, matrices and vectors, graphs, and just about any mathematical abstraction. You can use them to improve the typing in reflection-based code, references (i.e. java.lang.ref), ThreadLocal and similar constructs. You can use them in algorithms that perform operations over all of the above.
    Those are just a few off the top of my head. However, there is no argument over the fact that C++ templates are stronger. Java generics are just an extension to the type system. Everything you can do with them you could do without them by adding a few casts. C++ templates are a full blown Turing complete meta-programming language, as Toby put it. I don't think we really need that in Java. Generics smooth out the edges, but programming in Java is essentially the same as it was. Which is fine by me. I don't think the generics JSR had the mandate to revolutionize Java in the same way that C++ templates revolutionize C++ development. I would still like to see generics improve in the aspects of runtime type information and perhaps primitive type parameter support, but I wouldn't want them to take the C++ route.

    Gal
  45. You can argue both sides of the limited/safe vs. unlimited/unsafe. That's a good discussion to have with any language implementation.

    The first point is that you have just as much work with Java's version of generics as you had before.

    I agree with most of what you say. I just think that the safety aspect is somewhat exaggerated in all these debates about static typing, latent types, etc. It's much more about productivity than about safety. A feature like latent types forces everyone who uses a method to read through the implementation of that method to figure out what they can pass as an argument. That's much less productive than to have the author of the method state in a formal declarative way what the method expects.

    What I like about generics is not that I save a little bit of typing for the casts or that I don't risk class cast exceptions (they are rare anyway). I see generics as a kind of documentation that is always up to date. You have a lot more information "at your fingertips (c)" when you read List<File> files; than in the case of List files; because you don't know for example if it's meant to be names of files or File objects. That's also my problem with dynamically typed languages (although I admit that they are very powerful in other respects). In Python code, you come across pieces like this:

    def parsefile(file): ...

    That's very little information. You don't know if file is the name of a file or a File object or some other file like structure in the case of latent types. You don't know what it returns or even if it returns anything at all.

    Document parseFile(File f) ...

    This is much better. The difference is 7 letters. 7 letters that save me the work to read through the implementation code of a method that may have thousands of letters and complicated control structures.

    But I agree that Java does make some situations very cumbersome. If you have a method that is overloaded 20 times just to default parameter values, that's annoying. Default parameters and named parameters would save a lot of work. And a feature like boolean type expressions as planned for Perl 6 would also be great. You could replace four overloaded variations with just one:

    Document parseFile(File | String f, boolean validate = false) ...

    instead of:

    Document parseFile(File f, boolean validate)...
    Document parseFile(String f, boolean validate)...
    Document parseFile(File f)...
    Document parseFile(String f)...
  46. Alex, I am in agreement with much of what you've posted, and applaud the points you've made in this and other messages.

    However, with respect to the lack of type information at encountering something like
      def parsefile(file): ...
    in Python, I submit that Python's approach is more efficient in that it does not impose the burden of having to be explicit about providing such information all the time, whereas Java does.

    Specifically, the Python programmer can gauge where code needs to be explained and where it is amply transparent, and provide or not provide comments or longer explanatory names based on that common-sensical judgement.

    The Java programmer, on the other hand, is forced to spell out every bleeding thing all the time.

    So while Java code is easy to read insofar as having explicit variable types and method signatures, the drudgery and size of the resulting bloat (and other Java verbosity) detract from this read-friendliness significantly -- the nuggets of conceptual essence become obscured by the rock pile of other language foo.

    I find Python's expressive power, coupled with strategically placed comments and keyword arguments, a better solution not only for writing, which tends to be a rather faster and more pleasant process, but very much for reading as well.
  47. Mike,

    I've grown to have respect for you due to your ability to "shoot straight from the hip" as it were, but it seems to me that you (and several other people here) are practically burning Eckel in effigy out of some misfounded hatred for C++.

    For example, you write:

    If there are multiple libraries and you want to use a common generic over all of them, then you're just plain hosed. What you're postulating is that a 3rd party lib won't implement some interface, and yet it will just happen to magically have a method

    That's not true at all. If I want to use some template class or function against a type that doesn't fit the implied interface, then I can specialize the template function or class for that type. This prevents me from having to create some funky adapter class that I have to instantiate against every instance of that "non-compliant" type. It also means I only have to write that adapting logic one single time, instead of distributing it all over the place in my code, like I would have to do in Java.

    I believe that C++ templates have several problems - implied interfaces (=> no separate compilation) perhaps being the largest. That doesn't mean, though, that we should ignore what is good about them. With C++ templates, I can create very lean abstractions that operate, generically, over an unbounded set of types (or even bounded if I so choose - c.f. concept checking). Personally, I'd love to see Java add some form of flexible type matching for parametric parameters. Take a look at PolyJ and its where clauses as one kind of example.

    I'll just summarize by saying that C++ templates are very flexible and powerful, as well as very complex and difficult to use. We should learn from that, not strike out in fear.

    God bless,
    -Toby Reyelts
  48. I've grown to have respect for you due to your ability to "shoot straight from the hip" as it were, but it seems to me that you (and several other people here) are practically burning Eckel in effigy out of some misfounded hatred for C++.

    The criticism is harsh, but I believe it's warranted in this specific case. Read the last three paragraphs of Eckel's blog entry - his criticism of Sun, Java, and the generics facility (and by extension, their collective creators) is far harsher than you'll see here.

    I'd also argue that there are many people who hate C++ - and that hatred is not at all misfounded, but is instead rooted in long term experience with the language.

    That's not true at all. If I want to use some template class or function against a type that doesn't fit the implied interface, then I can specialize the template function or class for that type. This prevents me from having to create some funky adapter class that I have to instantiate against every instance of that "non-compliant" type. It also means I only have to write that adapting logic one single time, instead of distributing it all over the place in my code, like I would have to do in Java.

    In practice, you may need to create several specializations - and frankly those specializations look an awful lot like "funky" adapter classes. Admittedly you have to do more tap-tap-tap typing in Java to get the same effect, but that's just monkey coding.

    I'd claim alot of C++'s design is "fear-of" design. Reading D&E, Stroustrup uses many variations on the theme of "I was afraid if we did that then...". Alot of those fears were based on performance, or misfounded fears about how inheritance would use and adapt over time in on-going projects. There's alot of fear on things like, say, multiple inheritance and its use, that never came about.

    Java also has "fear-of" design in it as well, but it's roots are completely different. Mostly, it's fear of "this is overly complex", "this is more trouble than it's worth", etc. In addition, Java's designers learned alot of the pitfalls that C++ pioneered - fears that C++ people had that never panned out.

    I believe that C++ templates have several problems - implied interfaces (=> no separate compilation) perhaps being the largest. That doesn't mean, though, that we should ignore what is good about them. With C++ templates, I can create very lean abstractions that operate, generically, over an unbounded set of types (or even bounded if I so choose - c.f. concept checking). Personally, I'd love to see Java add some form of flexible type matching for parametric parameters. Take a look at PolyJ and its where clauses as one kind of example.

    I'll just summarize by saying that C++ templates are very flexible and powerful, as well as very complex and difficult to use. We should learn from that, not strike out in fear.

    I don't think anyone here is disputing the power you get with C++ templates, and that you can accomplish things with them that you can't in any other mainstream language. The question, as always, is: it is worth the cost? Do the conveniences and benefits of the system outweigh the considerable downsides? Also, most importantly - how many of the benefits are directly tied to the pitfalls (e.g. what power is enabled by #include files, is part and parcel of getting baroque error messages, is directly hooked into the bugs that still exist in compilers in the subtler parts of the mechanism)?

    Bringing it back to the original source of this discussion: to get what Eckel wants, what would you have to do to Java? Could you accomodate it without breaking existing bytecode? What characteristics of Java that we cherish today would have to be nuked or modified beyond recognition?

    Finally: what will the end effect of the end user be? Will we have hordes of Java programmers flocking to the facility and successfully using it to improve their projects? Or will have a handful of gurus who wield it like a dangerous but powerful weapon, and a gaggle of plain old developers who only tackle that code under dire threats of management? Will it be a source of liberation, or a source of endless bugs, head scratching, and throwing blunt objects at your monitor?

    -Mike
  49. I'd also argue that there are many people who hate C++ - and that hatred is not at all misfounded, but is instead rooted in long term experience with the language.
    People can have many years of experience with C++ and still have a misfounded hatred of it. I personally have over a decade of experience with the language, but that hasn't turned me into a "hater". :)
    that's just monkey coding
    So is the work that you have to do w/o auto-boxing/unboxing, enumerations, static imports, extended for loops, and Java generics.
    Let me touch in something that I didn't in the last post - In many well-written templates classes/functions, the "implicit interface" is abstracted out to a separate, optional type parameter. This is very similar to the way that Java uses interfaces, but much more optimal.
    For example, in both Java and C++, the sort() function takes an optional comparator. For each different type of comparator, the C++ application will generate an optimized version of sort() with the code for the comparison inlined. In Java, the comparator will be some object instance that implements Comparator. The virtual machine won't be able to do any inlining, because there is only one sort function (since that's how Java generics operates), and the type of comparator will vary by call - i.e. the call to the comparator is not monomorphic.
    I'd claim alot of C++'s design is "fear-of" design. Reading D&E, Stroustrup uses many variations on the theme of "I was afraid if we did that then...". Alot of those fears were based on performance, or misfounded fears about how inheritance would use and adapt over time in on-going projects. There's alot of fear on things like, say, multiple inheritance and its use, that never came about.
    I'm sorry, I first bought D&E about five years ago and have read it a few times since then (it's sitting on my bookshelf here), but I don't know what you're referring to. If there's any salient point to be taken away from D&E, it's that language design is a series of tradeoffs, and the majority of C++'s shortcomings are a direct result of one design decision - source and link compatiblity with C.
    In addition, Java's designers learned alot of the pitfalls that C++ pioneered - fears that C++ people had that never panned out.
    Again, what fears?
    I don't think anyone here is disputing the power you get with C++ templates, and that you can accomplish things with them that you can't in any other mainstream language. The question, as always, is: it is worth the cost? Do the conveniences and benefits of the system outweigh the considerable downsides? Also, most importantly - how many of the benefits are directly tied to the pitfalls (e.g. what power is enabled by #include files, is part and parcel of getting baroque error messages, is directly hooked into the bugs that still exist in compilers in the subtler parts of the mechanism)?
    You seem to be implying that flexible typing is somehow tied to implicit interfaces. My point was that flexible typing is a good thing - not a pitfall, and it is obviously not tied to implicit interfaces or C++'s implementation of parametric polymorphism. That's why I mentioned PolyJ's where clauses. You might also want to investigate Clu.
     
    Bringing it back to the original source of this discussion: to get what Eckel wants, what would you have to do to Java? Could you accomodate it without breaking existing bytecode?
    You don't ever have to break existing bytecode. The primary goal the Java author's had in mind was a free mixing of typed and untyped parameters. I'm still not sure that was a good idea. The new 1.5 language features have already caused significant virtual machine changes. Hence the need for something like Retroweaver - http://retroweaver.sf.net.
    What characteristics of Java that we cherish today would have to be nuked or modified beyond recognition?
    Do you consider PolyJ to be a "nuking" of the characteristics of Java?
    Finally: what will the end effect of the end user be? Will we have hordes of Java programmers flocking to the facility and successfully using it to improve their projects? Or will have a handful of gurus who wield it like a dangerous but powerful weapon, and a gaggle of plain old developers who only tackle that code under dire threats of management?
    You do know that there are a large number of "plain old developers" who decry even the limited version of generics that exists today, right? Do you side with them? I don't mind dragging the stragglers kicking and screaming into the 21st century. If they can't handle it, they need to find a new profession.
    Will it be a source of liberation, or a source of endless bugs, head scratching, and throwing blunt objects at your monitor?
    You must have had some very bad experiences with some very bad developers. My heart goes out to you.
    Back on topic - to repeat myself, I don't believe anybody is advocating that Java copy C++ templates verbatim. I personally wish we had a more flexible form of parametric polymorphism (preferably with explicit constraints), that provided acceptable performance when instantiated against both primitive and object types.
    God bless,
    -Toby Reyelts
  50. People can have many years of experience with C++ and still have a misfounded hatred of it. I personally have over a decade of experience with the language, but that hasn't turned me into a "hater". :)
    One might argue that if a significant number of people have many years of experience with a language and hate it, perhaps - just perhaps - that hatred is in fact not misfounded. It sounds crazy but maybe the language itself caused it!
    So is the work that you have to do w/o auto-boxing/unboxing, enumerations, static imports, extended for loops, and Java generics.
    I'd argue that these are features that were added that have convenience and other values that were bought with a very low level of extra language complexity. Most directly address readability as well. But if you start considering more aggressive proposals, the complexity level shoots up rapidly - and IMHO you're better writing monkey code than living with that language complexity.
    Let me touch in something that I didn't in the last post - In many well-written templates classes/functions, the "implicit interface" is abstracted out to a separate, optional type parameter. This is very similar to the way that Java uses interfaces, but much more optimal.
    For example, in both Java and C++, the sort() function takes an optional comparator. For each different type of comparator, the C++ application will generate an optimized version of sort() with the code for the comparison inlined. In Java, the comparator will be some object instance that implements Comparator. The virtual machine won't be able to do any inlining, because there is only one sort function (since that's how Java generics operates), and the type of comparator will vary by call - i.e. the call to the comparator is not monomorphic.
    This yet another C++ design argument which is really rooted in performance. For most Java apps, _it doesn't matter one bit_. On the whole, Java developers are not looking to sacrifice usability for something which is "much more optimal". I'd say that 95% of the users of generics won't give a damn if a call is in-lined or not, so long as it's good enough. So why design around the exceptional 5%?
    I'm sorry, I first bought D&E about five years ago and have read it a few times since then (it's sitting on my bookshelf here), but I don't know what you're referring to. If there's any salient point to be taken away from D&E, it's that language design is a series of tradeoffs, and the majority of C++'s shortcomings are a direct result of one design decision - source and link compatiblity with C.
    Toby, if you think about it for a moment you'll realize that it isn't true at all. How link compatible are the following with C: a C++ class w/ a virtual function; operator overloading; templates; exceptions; RTTI; new/delete vs. malloc/new. Answer: not at all.
    The reality is that link compatibility is a small part of C++'s design and only applies to the C-subset of the language. For the non-C pieces, the designer had a free hand. What you're missing are the other constraints: the decision to not use a single rooted hierarchy (fear that a single hierarchy would be too constraining), and the decision to always favor speed and small memory footprint over other considerations (fear of being too slow). There are a few cases of overlap - like compatibility of classes to C structs having to do with compatibility _and_ speed issues.
    But you can't look at C++ soley from the standpoint of C compatability - that went out the window with the very first virtual function. If you want to look at C++ facilities and Java facilities side by side, you have to take the triple-divinity on the C++ side of C compat, speed/memory, and single-rootedness all together. They are inextricably linked _by design_. They have far reaching implications to the design which simply do not apply to Java. In fact Java's Object class negates the most common uses of C++ templates.
    You don't ever have to break existing bytecode. The primary goal the Java author's had in mind was a free mixing of typed and untyped parameters. I'm still not sure that was a good idea. The new 1.5 language features have already caused significant virtual machine changes. Hence the need for something like Retroweaver - http://retroweaver.sf.net.
    As I've stated elsewhere, I think generics is the shakiest feature of Java 1.5. I'm still not sure if it's a good thing or a bad thing. However - I _am_ happy that they stopped where they did, and did not attempt anything like what Eckel refers to as "latent typing".
    Do you consider PolyJ to be a "nuking" of the characteristics of Java?
    I haven't seen PolyJ, so I can't comment.
    You do know that there are a large number of "plain old developers" who decry even the limited version of generics that exists today, right? Do you side with them? I don't mind dragging the stragglers kicking and screaming into the 21st century. If they can't handle it, they need to find a new profession.
    The implication is that generics is the 21st century. Some might say that, at least as encapsulted by C++, they were a failed experiement of the mid-90s. Given what we already have in Java, it is not clear that generics add enough to make it wortwhile. This is entirely different from C++ - C++ was rather screwed without some sort of generic mechanism. In Java you can always safely go to/from Object. In C++ that has never been the case, and templates were the only escape mechanism provided to get around it.
    You must have had some very bad experiences with some very bad developers. My heart goes out to you.
    Back on topic - to repeat myself, I don't believe anybody is advocating that Java copy C++ templates verbatim. I personally wish we had a more flexible form of parametric polymorphism (preferably with explicit constraints), that provided acceptable performance when instantiated against both primitive and object types.
    No, I've had some very bad experiences with a bad language :-)
    And having started getting into Groovy, I might say that time spent on something like, say, closures, would be time better spent in the Java world then parametric polymorphism.
    -Mike
  51. Mike (& others)
    Please take a look at this post:
    http://www.interact-sw.co.uk/iangblog/2004/03/14/generics
    The post is quite big and has a fairly accurate perspective on where Bruce Eckel has gone wrong in his analysis.
    --Dilip
  52. One might argue that if a significant number of people have many years of experience with a language and hate it, perhaps - just perhaps - that hatred is in fact not misfounded. It sounds crazy but maybe the language itself caused it!
    One might argue that since a large number of lemmings are jumping off a cliff, this lemming might as well also. Or one might be better off sticking to arguments with technical merit.
    I'd argue that these are features that were added that have convenience and other values that were bought with a very low level of extra language complexity.
    You are erroneously implying that a more flexible means of conformance acceptance requires some unacceptably large increase in complexity to the user.
    This yet another C++ design argument which is really rooted in performance. For most Java apps, _it doesn't matter one bit_. On the whole, Java developers are not looking to sacrifice usability for something which is "much more optimal". I'd say that 95% of the users of generics won't give a damn if a call is in-lined or not, so long as it's good enough. So why design around the exceptional 5%?
    Inlining is crucial to performance. The reason why Java developers can "not care" about performance, is because VMs are capable of performing tasks like inlining behind our backs. If language designers create an abstraction that the VM can not easily optimize, then our code will run unacceptably slow.
    Toby, if you think about it for a moment you'll realize that it isn't true at all.
    Mike, I think about language design all the time. Considering the course of this discussion, I'm not so sure that you do.
    The reality is that link compatibility is a small part of C++'s design and only applies to the C-subset of the language.
    Sure, it would have been possible to design a "dual-language" where one subset is C compatible, the other is not, and the communication between the two is complicated, goofy, and slow. Sounds eerily like JNI to me, but I don't know much about JNI.
    If you really did read D&E, you would know that Stroustrup would have designed an entirely different language if not for the design decision to maintain C compatibility.
    In fact Java's Object class negates the most common uses of C++ templates.
    To quote you: "The short response to that is bullshit." If that's what you think C++ templates are for, then you misunderstand the purpose of parametric polymorphism.
    As I've stated elsewhere, I think generics is the shakiest feature of Java 1.5. I'm still not sure if it's a good thing or a bad thing.
    It's not whether generics is good or bad. It's accepted that parametric polymorphism is good. It's not yet accepted that the Java generics implementation of it is "good".
    I haven't seen PolyJ, so I can't comment.
    I think you should do some more investigation into the proposed alternatives for Java Generics (and implementations of parametric polymorphism in other languages), before spending more time in discussion on the existance of viable alternatives.
    The implication is that generics is the 21st century.
    No, people who weren't in over their heads would say that parametric polymorphism is 70s technology that has been widely implemented in many languages outside of C++ and has finally started becoming mainstream. Hence the need for "mainstream programmers" to start taking advantage of it.
    In Java you can always safely go to/from Object. In C++ that has never been the case, and templates were the only escape mechanism provided to get around it.
    Java without generics is just as broken as C++ without templates.
    No, I've had some very bad experiences with a bad language :-)
    Apparently, nothing I say will change your opinion, which is fine. To each their own. I just wish you wouldn't be blinded by your hate for the language.
    And having started getting into Groovy, I might say that time spent on something like, say, closures, would be time better spent in the Java world then parametric polymorphism.
    While I don't agree that closures are "better" than parametric polymorphism, I can at least agree that language support for them would be nice.
    God bless,
    -Toby Reyelts
  53. One might argue that since a large number of lemmings are jumping off a cliff, this lemming might as well also. Or one might be better off sticking to arguments with technical merit.
    Toby, you may recall that you started this portion of the thread by saying that people have a "misfounded hatred" of C++. My only point in this regard that it is _not_ misfounded, but is grounded in real life experience. Feel free to disagree, but don't say that it's misfounded.
    You are erroneously implying that a more flexible means of conformance acceptance requires some unacceptably large increase in complexity to the user.
    I don't know what you mean by "conformance" here. The underlying point was rather simple I thought - certain features imply more language complexity than others, both to understand and to implement. Look at the specification for C++ templates, then look at, say, enums (or even Java numerics). You can quantitatively say that templates are much more complex based on the specs alone.
    Inlining is crucial to performance. The reason why Java developers can "not care" about performance, is because VMs are capable of performing tasks like inlining behind our backs. If language designers create an abstraction that the VM can not easily optimize, then our code will run unacceptably slow.
    Many Java programs run acceptably fast today despite your arguments. The difference between C++ and Java in this regard is that C++ relies on low-level mechanisms exposed to the coder which are easily targetted to a compiler. Java relies on smarter JVM's and things like Hotspot.
    Sure, it would have been possible to design a "dual-language" where one subset is C compatible, the other is not, and the communication between the two is complicated, goofy, and slow. Sounds eerily like JNI to me, but I don't know much about JNI.
    If you really did read D&E, you would know that Stroustrup would have designed an entirely different language if not for the design decision to maintain C compatibility.
    Toby, it would have been entirely possible to design a language which retained C link compatibilty and not be "goofy and slow", or look anything like JNI. Why do all methods default to non-virtual? Hint: it has nothing to do with compatibility. Why are classes and structs interchangable? Hint 2: it has nothing to do with compatibility. Why do templates have to do with C compatability? Hint: nothing.
    Here's a simple exercise: find all the points in C++ that are _completely_ incompatible with C linkage. Now explain how C linkage has anything to do with these things. Answer: they don't.
    I realize the above is rather aggressive, but you're resisting a fundamental point of C++: C linkage and compatability is only one small part of the design. Most C++ code has zero C compatability - C programs don't have a prayer of calling into your polymorphic multi-inheritance chain with templates. I'm sure you've read D&E, but I don't think you're seeing that the language design goes beyond mere C compatability. Many design decisions have _nothing_ to do with C compatability.
    If you want to argue the point, let's keep the argument somewhat on point: please argue for me how the C++ template is designed with C compatability in mind. Please - show us how C++ templates may be used from C code.
    Me:
    In fact Java's Object class negates the most common uses of C++ templates.
    To quote you: "The short response to that is bullshit." If that's what you think C++ templates are for, then you misunderstand the purpose of parametric polymorphism.
    That wasn't the main point. Here's a mental exercise: imagine C++ did have a single rooted hierarchy for a moment. Imagine all C++ objects have a common Object root, with hashCode() and equals() defined. Imagine further that something like Comparable exists. Finally imagine true runtime type checking.
    In this scenario - how much has the need for C++-style templates diminished? How many problems that C++ developers solve today with templates could be solved with alternative mechanisms? All that's required is three things: a single rooted hierarchy, interfaces, runtime type checking. How valuable do Templates look now?
    No, people who weren't in over their heads would say that parametric polymorphism is 70s technology that has been widely implemented in many languages outside of C++ and has finally started becoming mainstream. Hence the need for "mainstream programmers" to start taking advantage of it.
    I think you'd find alot more people who said that runtime type checking is finally becoming mainstream. Java, for me, is a nice hyrbrid which features extensive static type checking but also features the real safety of dynamic typing as well. A combination of static and dynamic typing is _far_ more valuable to me than templates would ever be - or the rest of C++'s unsafe and buggy type system.
    On the topic of generics, a commenter on Eckel's blog said it rather well - I'll paraphrase here. You'll find that C++'s implementation of generics should not be seen as the standard mechanism for achieving this functionality, but instead is the odd duck which is quite different from how most languages achieve the same goals.
    BTW, to correct a misconception: I've been using C++ since 1989. Your vague implication that I am "in over my head" is _way_ off the mark.
    Java without generics is just as broken as C++ without templates.
    This shows that you don't understand dynamic type checking.
    C++ prior to templates: collections were (void *) and you did casts to the object you hoped it would be, and more often than not would core dump if you were wrong. In Java, everything is an object, and a bad cast equals a ClassCastException. As someone who supports many production servers, I can tell you definitively that a thread that occasionally throws a ClassCastException is _infinitely_ preferable to a core dump. And if you could add to infinity, it's even more preferable to corrupted data which does not lead to a core dump. The plain, cold fact is that C++ was completely unsafe when it came to usages like Collections pre-templates. Java, sans Generics, is dynamically typesafe at runtime. The difference is _huge_. Until you understand the difference, you will never understand my arguments.
    -Mike
  54. Feel free to disagree, but don't say that it's misfounded.
    I say that people's hatred for C++ is misfounded, because they don't understand how to use it as the right tool for the right job. You seem to fall into that category.
    I don't know what you mean by "conformance" here.
    The term, conformance, refers to the tests that the compiler performs to determine whether a particular value is an acceptable parameter.
    The underlying point was rather simple I thought - certain features imply more language complexity than others, both to understand and to implement.
    You seem to be missing the point. The original complaint was that Java generics are too inflexible. My point is that increasing the flexibility of Java generics, does not necessarily lead to an unacceptable increase in their complexity.
    Many Java programs run acceptably fast today despite your arguments.

    Again, you're missing my point. The Java language only performs "well enough", because the abstractions we have created aren't too complex for the virtual machines to optimize - well, at least for a certain class of problems, anyway.
    Why do all methods default to non-virtual?
    Yes - another important design decision of C++ is that "you don't pay for what you don't use". I recently experienced the usefulness behind this decision. When I have to load 100 million objects in memory, Java chooses to chew up 800M in pure object header overhead. With C++ there is no penalty.
    You may say that that kind of program is unusual, yet I fear it is becoming more and more the norm. As our machines grow more powerful, we choose to solve more complex problems with them.
    If you want to argue the point, let's keep the argument somewhat on point: please argue for me how the C++ template is designed with C compatability in mind. Please - show us how C++ templates may be used from C code.
    I would not be surprised that if Stroustrup had designed a language free from C, that language would not have an implementation of parametric polymorphism that looks like C++ templates. Of course, you can just ask him yourself. He's pretty friendly. For example, he sympathized with the problems I was having related to explicit template specialization.
    But this is already off the point - the real point of all of this was that Java generics is too strict. Eckel gave an example of how C++ templates are less strict. There are many other implementations of parametric polymorphism out there that are less strict. You're drawing some sort of false conclusion that less strict generics == C++ templates == bad.
    In this scenario - how much has the need for C++-style templates diminished?... How valuable do Templates look now?
    Not at all. Here's a thought exercise for you. How is a map implemented in C++ without templates, different than TreeMap implemented in Java without generics?
    You really shouldn't try to argue about the general usefulness of templates, because they serve more purpose than parametric polymorphism. They are a first-class, turing-complete, meta-programming facility. Java has no match for this facility at all.
    I think you'd find alot more people who said that runtime type checking is finally becoming mainstream.
    Probably because the large majority of practicioners of software construction aren't educated enough to have even heard the term, "parametric polymorphism", even when they use the feature on a daily basis.
    Java, for me, is a nice hyrbrid which features extensive static type checking but also features the real safety of dynamic typing as well.

    C++ also has dynamic type safety - it's called dynamic_cast<>. But you only pay for it if you use it.
    You'll find that C++'s implementation of generics should not be seen as the standard mechanism for achieving this functionality, but instead is the odd duck which is quite different from how most languages achieve the same goals.
    I never said that C++ templates were the "standard". You were the one implying that parametric polymorphism was introduced with C++ templates. And again, you're veering way off the main point - most of the other mechanisms you'll see out there are more flexible in type conformance than Java generics.
    BTW, to correct a misconception: I've been using C++ since 1989. Your vague implication that I am "in over my head" is _way_ off the mark.
    I wasn't implying anything about your C++ skills. Rather, I was suggesting that you didn't know much about programming language theory or design. You've sparked my curiosity, though. When was the last time you wrote a C++ program?
    This shows that you don't understand dynamic type checking.
    Now you're just plain being funny Mike. Accusing me of not understanding dynamic type checking. Excuse me while I catch my breath here (laughing too hard)... Here's a hint - download any of the code I've written, and then tell me what I do or don't understand.
    C++ prior to templates: collections were (void *) and you did casts to the object you hoped it would be, and more often than not would core dump if you were wrong. In Java, everything is an object, and a bad cast equals a ClassCastException.

    In both cases I have to write casting code that narrows a root type to a specific type. In both cases, I can write an incorrect program that performs the wrong cast. Then both programs are broken and behave unpredictably. Yes, the Java program will throw an exception, and that is, admittedly, better than nothing. I am not, however, seeing this as an argument against flexible parametric polymorphism.
    Until you understand the difference, you will never understand my arguments.
    Still laughing, Mike. No hard feelings, though.
    God bless,
    -Toby Reyelts
  55. <toby>
    Let me touch in something that I didn't in the last post - In many well-written templates classes/functions, the "implicit interface" is abstracted out to a separate, optional type parameter. This is very similar to the way that Java uses interfaces, but much more optimal.
    </toby>
    I don't think C++ templates "force" bad designs. However, I do think that Java generics promote the type of good design you are describing more than C++'s mechanisms.
    <toby>
    For example, in both Java and C++, the sort() function takes an optional comparator. For each different type of comparator, the C++ application will generate an optimized version of sort() with the code for the comparison inlined. In Java, the comparator will be some object instance that implements Comparator. The virtual machine won't be able to do any inlining, because there is only one sort function (since that's how Java generics operates), and the type of comparator will vary by call - i.e. the call to the comparator is not monomorphic.
    </toby>
    The virtual machine can do inlining in this case. The virtual machine can compile an optimized version for a specific comparator (including inlining the code of that comparator) and use that version in places where it knows the type of comparator that will be used. This applies, particularly, wherever a C++ compiler could do inlining (because the type of template being instanciated is always known at compile time and therefore available to the VM).
    So called "Generic Algorithms" can also be implemented using the "Generic Algorithm" design pattern (abstract class which delegates the "generic" part to subclasses) and again, modern VMs can optimize and inline the code by creating seperate versions of the superclass' methods for each subclass.
    Optimizations performed in this way have the advantage that they do not expand all code indiscriminately, but rather aggresively optimize only the "hot spots". This achieves reasonable optimization without blowing the code size to unreasonable propotions.
    <toby>
    You seem to be implying that flexible typing is somehow tied to implicit interfaces. My point was that flexible typing is a good thing - not a pitfall, and it is obviously not tied to implicit interfaces or C++'s implementation of parametric polymorphism. That's why I mentioned PolyJ's where clauses. You might also want to investigate Clu.
    </toby>
    I'm not familiar with Clu. PolyJ's where clauses are indeed more flexible, but they don't interact as smoothly with the existing type system. I think the question is just how much practical need is there for this feature. I agree with Mike that in Java it has little practical importance, because Java classes are not likely to have methods with identical signatures and reasonably similar semantics unless they implement a shared interface. In C++ I think the vast majority of cases where this happens is with operator overloading where you have predefined names and intuitive semantics. Can you give a real-world example where this would be useful in Java?
    I also want to point out that generic algorithms can still be implemented using the "generic algorithm" design pattern without requiring the creation of wrappers. It might be a little less convinient, but not overly so IMO. Certainly not if "where-clauses" are seldom required (which I believe is the case).
    <toby>
    You don't ever have to break existing bytecode. The primary goal the Java author's had in mind was a free mixing of typed and untyped parameters. I'm still not sure that was a good idea. The new 1.5 language features have already caused significant virtual machine changes. Hence the need for something like Retroweaver - http://retroweaver.sf.net.
    </toby>
    I don't think compatability with older VMs was the key consideration (in the final release the 1.5 compiler isn't even going to support "-target 1.4", is it?). The main consideration was that in order to allow a smooth transition to generic types, old code must be able to accept generics types as inputs, and it's output must be easily convertible to a generic type. I think the JSR does a very good job at achieving this goal. Future versions may add more features at the expense of weaker compatability - for instance NextGen is an extension of GJ with runtime type information that may be considered for future JDKs.
    Regards
    Gal
  56. The virtual machine can do inlining in this case. The virtual machine can compile an optimized version for a specific comparator (including inlining the code of that comparator) and use that version in places where it knows the type of comparator that will be used.
    Write a short program for me for which a real, production VM will perform this optimization.
    Optimizations performed in this way have the advantage that they do not expand all code indiscriminately, but rather aggresively optimize only the "hot spots".
    C++ compilers do not "indiscriminately" inline. In fact, they perform more accurate analysis of the appropriateness of inlining than a VM. HotSpot has to limit its analysis to "hot spots", because it doesn't have the CPU cycles to waste to perform more extensive analysis.
    I'm not familiar with Clu. PolyJ's where clauses are indeed more flexible, but they don't interact as smoothly with the existing type system.
    Have you ever written any code using PolyJ? How do you know how well they interact?
    I don't think compatability with older VMs was the key consideration (in the final release
    the 1.5 compiler isn't even going to support "-target 1.4", is it?). No, there was an emphasis on binary compatibility. Originally, everyone was told that it was going to be binary compatible, initial release of the compiler were binary compatible, then somewhere along the lines, they dumped binary compatibility. And to repeat myself, that is why Retroweaver exists.
    old code must be able to accept generics types as inputs, and it's output must be easily convertible to a generic type.
    Yes, that is a paraphrase of what I said.
    NextGen is an extension of GJ with runtime type information that may be considered for future JDKs.
    NextGen is a good example of another fault in Java generics. The lack of type reification in Java generics accounts for all sorts of broken, unintuitive, and inflexible behavior. Another example, for Mike, that language design is a series of compromises.
    God bless,
    -Toby Reyelts
  57. <toby>
    Write a short program for me for which a real, production VM will perform this optimization.
    </toby>
    I don't want to get into an argument about the performance of Java in general or generics in particular. I don't think it is of any practical importance for the vast majority of applications. My point is that the design of the language - including generics, allows such optimizations. Whether or not VMs will implement them, and to what extent, depends on market requirements more than anything else.
    <toby>
    C++ compilers do not "indiscriminately" inline. In fact, they perform more accurate analysis of the appropriateness of inlining than a VM. HotSpot has to limit its analysis to "hot spots", because it doesn't have the CPU cycles to waste to perform more extensive analysis.
    </toby>
    I can't respond to general statements about "C++ compilers" and "VMs". Obviously the level of optimization depends on the specific product at hand. However, for long running server applications the VM is likely to have sufficient time to aggressively optimize hot spots. It also has runtime information that allows it to optimize adaptively - information that C++ compilers cannot have (for instance, because it may depend on user inputs). On a practical level I don't think any of this is very important.
    <toby>
    Have you ever written any code using PolyJ? How do you know how well they interact?
    </toby>
    Yes I have (just out of curiousity). I felt that the interaction with the existing type system is not very smooth. For instance if I write a template and I want to operate on Comparable classes, and use existing functions such as sort (the current, non-generic version) PolyJ does not provide a very elegant solution. Such issues do not arise when you use a "generified" version of the code all around. But on interaction with the existing type system I did find it to be a little awkward.
    <toby>
    No, there was an emphasis on binary compatibility. Originally, everyone was told that it was going to be binary compatible, initial release of the compiler were binary compatible, then somewhere along the lines, they dumped binary compatibility. And to repeat myself, that is why Retroweaver exists.
    </toby>
    Binary compatability is certainly a nice feature. However, the JSR explicitly states that "It is explicitly not required that the system provide downward binary compatibility: it is not necessary that class files compiled under the generic compiler should run on previous releases, whether they use generics or not."
    Regardless of what was the emphasis, in my view the emphasis should have been on enabling smooth transition from non-generic code to generic code, by allowing the two types to be intermixed easily. I think the spec did meet this requirement.
    <toby>
    NextGen is a good example of another fault in Java generics. The lack of type reification in Java generics accounts for all sorts of broken, unintuitive, and inflexible behavior. Another example, for Mike, that language design is a series of compromises.
    </toby>
    I agree that NextGen adds some important features. However, it also comes at a cost of less compatability. I hope NextGen (or something similar) gets included in future versions. However, to kick off the transition into generics I think the highly compatible version currently that made it into JDK1.5 is a good compromise.
    Regards
    Gal
  58. Gal makes good points. I think the main differences here between C++ and Java are the lack of a common base class in C++, the lack of a common Comparable interface and the like in C++, and especially the fact that C++ has operator overloading.

    In C++, there are a lot of implied interfaces, or conventions, that are often the same between different libraries. For example, overloading the '<' operator in C++ is equivalent to implementing Comparable in Java. Ditto for '==' operator and Object.equals, etc. Also a lot of C++ math libraries obviously use '+' to mean addition, etc. Since there is only a fixed set of operators, two different third party libraries will often overload the same operator to mean the same thing. But it's much less likely that two different Java libraries will come up with the same method name to mean the same thing, as there's an infinite number of possible method names.

    So, 'latent types' are useful in C++, not so useful in Java.

    Steve
  59. This is what I'll take home[ Go to top ]

    This is what I'll take home from the discussions.

    Either you write an interface, or you write a document.

    Yes they are the same; for a book writer, the later one is
    even better, because he can hide the document in the main
    text, rather than listing the interface code in a gray box,
    and scares the sh*t out of the newbies.

    The people who write code for other inferior purposes, i.e.
    to run and maintain, may find it differently.
  60. examples to clarify generics[ Go to top ]

    Till Alexander's post and Mike Spillie's acknowledgement of it, and all of Mike's comments till then, it was a great reading. It clarified with words, many hunches. These posts address many important detail's about Bruce Eckle's recent 'typeless' crusade, and about C++ model.

    First, I've not seen C++ template, but yet, started yearning for it in recent times, with some vague inadequacy of Java. When heard about Generics couple of years back, that looked like my solution all along. When last month I downloaded 1.5-beta and explored for using it in the product our company plans to release 4-6 months from now (god save them :), following were my questions.

    1.
    Is Generics useful for anything other than Collections?
    2.
    Type safety is Java, and if so, I cannot use any method of the parameterized object (except Object), what use can it have other than for input and output variable, assignment, and Object usage?
    3.
    If ".. extends interface>" is the solution for it, how is it better than plain OO model of using interfaces?

    All along one thing was beyond doubt for me, Type Safety is elegant. But when Eckle talked about unit test-supported-dynamic-typing with Python, I liked it (but how many tests should I write to check the input types, whereas in java et al, I can get it free?), as I like python's cool array related syntax, Ruby's closures, and now Groovy's. I wanted to resist accepting Eckle, from whose 2nd ed. TIJ (man! how did you cook up the title of this article, polite nudge!) I learnt java. It was a hard lure to resist, coming from the most eloquent teacher I have seen, and who uses the the scripting language with most sugary syntax for example. Anyway, the need of typesaftey is a deeper battle, and this discussion has given me some nice pointers, against typelessness.

    The things that are saving me to use Generics without guilt, are the simple critical examples, which I hunted for early on.

    Before that, one consistent mood I see in java is compile time checks (Type Safety, structured code, OO, etc.). Whether you agree with it or not is a different debate. But if you agree, many things are consistent within Java. Also, not so clearly, typesafety means 'reducing casts'. The attempt of Java, is to remove casting, wherever an elegant option is available. Like what it did to 'goto'. Like what it did to 'inheritance'. It salvaged the deep loop jumping from goto, and dumped it. It salvaged 'polymorphism' from Inheritance, and dumped multiple inheritance. And like wise, from Eckle's explanation of C++ templates, it salvaged 'Generics' and dumped dynamic types, which is just not java.

    1.
    If you are thinking Generics is not usual for anything other than Collection, consider this.

    MyClass myClass = MyClass.class.newInstance();

    How else can you avoid casts, and yet be type safe! And note, this has nothing to do with collections.

    2.
    The answer to the too simplistic just-Object-based-parameters, is of course the now famous, or notorious <? extends MyClassOrInterface>
    syntax.

    By the way, I think the reason (eckle questions this) why 'extends' keyword is more appropriate is, because MyClassOrInterface can in fact be a Class too. Somehow, 'implements' very clearly signifies interfaces, while 'extends' means 'extension' of an interface or a class.

    And if you want type safety, barring syntax debates, this is THE way to model the typesafe Generics paradigm. C++ template, in this light, seems to be only a polished escape to code in scripting language, to generate code (maybe that is why they named it as Templates?! to create a code template, and forget type safety).

    3.
    Finally and importantly, if interface is needed for extending the usefulness of Generics, why not plain interfaces? consider the following.

    public interface MyInterface {
    public void doSomething();
    }

    public class MyClass implements MyInterface {
    public void doSomething() {
    }
    }

    public class MyGenericClass<T extends MyInterface> {
    public T useInterface(T t) {
    //Note this -- 1
    myInterface.doSomething();
    ....
    return t;
    }
    }

    MyGenericClass<MyClass> myGenericClass = new MyGenericClass<MyClass>();
    MyClass myClass1 = new MyClass();

    //And now lo behold! Note this -- 2
    MyClass myClass2 = myGenericClass.useInterface(myClass1);

    How else can you pull this castless and typesafe assignment! And it will work without casting for any class that implement the MyInterface.

    Ramu
    http://www.TatTvum.com