Evolving Java Without Changing the Language
infoq.com
infoq.com
It's in the spirit of machine code in REM statements. A ghastly, reanimated spirit.
/**
<commands>
<command name="SOD" order="NEW">
<parameter slot="P2" name="N" value="4811980" />
<parameter slot="P2" name="N" value="4811980" />
</command>
</commands>
*/
@DomLiteral static Document testCommands = DomLiterals.testCommands();I wonder how far you could take this. A language like Duby gives you Ruby syntax, while producing identical bytecode as Java. If you could translate Duby into Java source code, and parse and show it as Duby, your IDE could let you program in Duby without anyone else on your project even noticing.
Then you're in effect relegating Java to object code.
I actually am thinking of writing a compiler for a custom language that will be translated to Java. It will be a lot like C#, but simplified, with a lot of the cruft removed (like the requirement to define a Main class with a static main, or the requirement that all code should be wrapped in a class, but reified generics is something I want although I'm not sure if it's feasible).
I don't like Duby, I don't like Ruby for that matter. Mostly because the syntax is not as extensible as I'd like and because the variables don't have lexical scope.
Much easier to just setup a simple macro system to run over source files and replace things before compilation.
If you ask some people what is the practical use of certain concepts you get answers like "Uh, you can't even understand why this is so good, because your brain doesn't work in my language, see the Blub Paradox... But see how nice my factorial function looks!"
Ironically, I think Java is now one of the most used language in academia, which seemed to have been explicitly an "anti-goal".
Not to say that it doesn't have merits. It's fast enough, it's cross-platform, it has good tools and good libraries. But this wasn't the case until Java 1.4 was released. And its popularity has more to do with Sun's marketing campaign than anything else.
If you can do proof by assertion, so can I:
Java's popularity has more to do with properties of the language than marketing.
Both are true.
Java is absolutely chock-full of "things that sounded like a good idea" -- unboxed types, single inheritance, interfaces without the possibility of shared implementation (or state), public / private / protected / default, positional constructor arguments, "Type foo = new Type", etc. Actually, none of those even sound like good ideas. (But I guess after the brain damage that was C++, it was inevitable that The Industry would overcorrect. Hopefully we've learned one important lesson from Java -- making the language "simple" won't stop people from writing bad code, but it will stop people from writing good code.)
Java also popularized some very very experimental ideas, like virtual machines and automatic garbage collection. (And I am very thankful for both.)
So now that I think about it, I don't get that quote at all.
(Another thing that could have gone this way are parameterized types representing null/not-null, like Haskell's Maybe or Either. It seems like a lot of work to annotate every type that can possibly be null, but it is not onerous at all in real life. A few functions are Maybe/Either, but most aren't; and for the price of an annotation you can be sure that you will never get a "NullPointerException". Checked exceptions could have turned out this way, but something about Java, Java's libraries, or imperative programming in general made this not so.)
With checked exceptions it is also not easy to be a good OOP citizen and not break encapsulation, since checked exceptions become a part of your interface.
"Algol is a blight. You can't have fun with Algol. Algol is a code that now belongs in a plumber's union. It helps you design correct structures that don't collapse, but it doesn't have any fun in it. There are no pleasures in writing Algol programs. It's a labor of necessity, a preoccupation with the details of tedium."
Type foo = new Type() is a bad idea? why?
Shape myShape = new Rectangle(); seems pretty logical to me. If you want to use static types, you can't really argue against that syntax.
single inheritance sidesteps lots of potential issues, and interfaces are cleaner IMHO
>> "but it will stop people from writing good code."
I'd also strongly disagree with that. Arguing that Java prevents you from writing good code is nonsense. As much sense as saying using French prevents you from writing a good story. I've seen some brilliant code written in Java (If you steer well clear of 'enterprise' etc).
Note, I did not say never. But don't give me a line about how you use it all the time. You don't. If you think you do, go grep over your code; you may think you do but I still bet you don't, it's probably a perceptual bias where you notice the exceptions out of proportion to their actual occurance. If you still pass that test, congratulations, I was wrong, you're one of the five people who do it all the time. You should probably stop, as something is probably very wrong with your design.
Meanwhile, optimizing the syntax for this case is a terrible optimization decision. That's the real problem, forcing literally millions (conservatively) of type annotations that are totally redundant.
"I've seen some brilliant code written in Java (If you steer well clear of 'enterprise' etc)."
You don't see the brilliant code that was never written because Java doesn't offer you the ability.
I also point to the "brilliant code" that isn't Java code at all, but compiler and bytecode hacks, which I suspect had you taken the time to mention some examples of "brilliant code" would have been in the list. That's not brilliance you can credit to Java, it's simply stuff that was so desirable it had to be done despite the fact Java didn't permit it.
Shape foo = new Shape();
is often written separately:
Shape foo;
// Further down
foo = new Shape();
So in that instance it's easy to see in both places what foo is.
If you do have issues with the duplication, it wouldn't be crazy hard to just write a pre-processor to insert the Types on the left if you haven't specified them, before compilation.
I simply don't buy the notion that you can't write great code in language X. Language is largely irrelevant to how great the code is or isn't. Also irrelevant to how well you can scale, how successful your startup will be, etc etc
Great coders write great code. Languages are just ways of communicating that great code with machines, and other people.
Some people have managed to put out the necessary incentives, so such code does exist, but seeing a putatively great coder reach for Java as their first choice without such incentives is like seeing a putatively great coder reach first for Cobol... it pretty much proves they aren't a great programmer. Even if you want the JVM libraries, there are better ways.
Yeah, it's an opinion, but it's one from someone with the aforesaid experience in a lot of languages. It's like saying a great marathon runner can run a marathon in concrete shoes. Yeah, maybe they can, but seeing them choose it of their own free will would say an awful lot about their wisdom in shoe choice. Java is a language that simply loathes great code.
Besides, you seem to have missed the core point that optimizing the language for a case that doesn't come up often is a misfeature, no matter how you slice it. Optimize the language for the case that occurs most. "var X"-type syntaxes cover that. "var X" still works in your example, too; there's no reason that a smart compiler can't deal with that sort of separation between declaration and initialization.
Personally it seems like some programmers at some point dislike being restrained by anything and become slightly high and mighty about the whole subject - the whole "Unless you use lisp, you can't understand why lisp is the best language" BS.
Why is "var X"-type syntax better? Why does that create better code :/ Sorry, I don't buy it. "var X" would be less precise, and less useful than specifying the explicit type you want.
FWIW, I just checked for fun my own comet server written in Java against tornado (Python). Very similar functionality, except the Java one is a smaller codebase. It's as verbose as you want it to be.
Of course we all know that Lisp is just a semi-imperative hack. Real programmers use something more pure like Clean or Haskell nowadays.
Anyway, I just wanted to applaud you two for the civility ouf your discourse. It was an informative read.
Maybe before ML appeared in 1973... after that it's just Bondage and Discipline to repetitively declare non-machine types.
Well, I know it prevents me from writing good code. I start writing it and it becomes so repetitive that I just get bored and do something else. I can't see progress quickly, and doing simple things involves a lot of work. (A new test case in Perl is one line in a file. In Java, it's an entire new file.)
I've also noticed that I can cleanup legacy Perl codebases very quickly, but legacy Java codebases are often a total loss. There is often Just Too Much slightly-tweaked editor-written boilerplate, instead of actual abstraction. It hurts to look at.
There is a reason that lots of smart people don't use Java and it's not because they hate popular languages.
Right, but everyone is different. It depends on how you think and your personal taste. Some people look at 'public void foo()' and apparently recoil in horror. I look at significant whitespace, or foo => |something| etc type syntaxes and recoil in horror.
Lots of people also claim that no one can be productive writing in pure assembly language, and while that may be true for many programmers, it's certainly not true for everyone.
I don't think you can really say which language 'smart people' use. Obviously there's a lisp/functional bias here on HN, but I'd be very surprised if 'smart people' congregate toward any one particular language.
I still think good programmers are defined by the code they write, rather than which language they choose to write it in.
What matters is how many tokens you need to say something simple. Say you have a list of words, and you want to sort it by length. That's one line of code in Perl, Python, Ruby, Common Lisp, Haskell, etc. It is not one line of code in Java.
When every simple concept in your program requires a new class with a new file, it just becomes unmanageable, even with the best tools. That's why I can't be productive in Java. (Java also doesn't allow quick hacks; private is private, so fuck you. This means that prototypes have to be "as good as" the real implementation, which simply means that you don't prototype. The resulting software sucks accordingly.)
But anyway, I know you have a pretty good app written in Java. I would like to hear more about how you deal with it sometime.
I don't really subscribe to the "Less tokens to say something = always better". As long as it's easy to see what's happening... I do agree collections, and specifically sorting is one area Java is slightly lacking, but it's not a big deal really.
Hopefully I'll release the comet server open source sometime. It's similar to tornado in functionality, but slightly smaller codebase. I think it'd surprise a few people about how you can write Java code if you choose to.
Yeah, well, you're also unable to show French to an English speaker without having to explain the grammar rules and define each word. To use a language, you have to learn it. To read a language, you have to learn it. (Reading is the harder of the two. When you are programming or speaking, you can say things the way you want to. When you are reading or listening, you have to understand the other person's way of speaking.)
Less tokens to say something != always better
I definitely agree with you there. Regular expressions are a good example of this -- way too much optimization for typeability at the expense of readability. It is much easier to read an expression when you have a little bit more verbosity; in the regex domain there are things like rx (Emacs Lisp) or Parsec / PArrows (Haskell). Easy to read, not too hard to write (is "\w+" much harder to type than "(many words)"? no.)
But it is also possible to be too verbose. If you just want to say:
my @sorted = sort (length $a <=> length $b) @list
you really don't want to have to write: // file SortableByLength.java
public interface SortableByLength implements Sortable, HasLength {}
// your app code
List sorted;
Sorter sorter = new Sorter<SortableByLength>();
sorted = sorter.customSort( list,
new SortFunction {
public Ordering compare(SortableByLength a, SortableByLength b){
return a.length().compareTo(b.length());
};
}
);
By the time you have written that, you have forgotten what you were actually trying to program. (Upon proofreading this, I realize that the sorters could all be specialized to a concrete type, and that would save a bit of typing. But when you factor out the anonymous class, you will want it to be generic.)It also worth noting that the extra typing there is not to get type safety; I can get the same type safety with a lot less typing in Haskell:
sorted = sortBy f list
where f :: (HasLength a, Sortable a) => a -> a -> Ordering
f x y = compare (length x) (length y)If you look at the Java code you've pasted, there's not really anywhere you could typo a character or 2, and it still compile. You'd have to typo a lot, or completely write it in the wrong way for it not to work. It's absolutely explicit and clear what it's doing.
With the Haskel code, you could mistype or misread a single character, and it'd completely change the meaning of the code.
I agree it's more verbose than the Haskel example, but also IMHO more solid/safe.
It's nearly impossible to get past the type system unless your code is correct, especially for small chunks of code.
The place where this code will screw up is in the logic of the comparison routine. It will be difficult for the types of x and y (or a and b in the Java example) to be wrong, but it is easy to say:
compare (length x) (length x)
or compare (length y) (length x)
instead of compare (length x) (length y)
Your tests will catch this, though.But the essential point is that after HelloWorld a C/C++ programmer with a JavaDoc can pretty much start churning code. The immediate consequences, way before getting to esoteric disputes about single/multiple inheritance, were only positive: the code was portable, memory problems disappeared, the documentation was in one place, the library was standard etc. The language itself, for the ordinary programmer of the time, was just simplified C++.
Why not strengthen the core language ?
Actually this philosophy worked better for future-proofing the platform in a weird way. God knows how they would've crippled the JVM if their generics support wasn't made through type-erasure for example.
And so the JVM bytecode is pretty light, now only if they added tail-calls and continuations. But I don't think that will happen too soon.
Why not put that effort into a newer language running on the JVM ?