Blogger's Block #4: Ruby and Java and Stuff
Part 4 of a 4-part series of short posts intended to clear out my bloggestive tract. Hold your nose!
Well, I held out for a week. Then I read the comments. Argh! Actually they were fine. Nice comments, all around. Whew.
I don't have any big themes to talk about today, but I've got a couple of little ones, let's call 'em bloguettes, that I'll lump together into a medley for today's entree.
I was in Barnes today, doing my usual weekend stroll through the tech section. Helps me keep up on the latest trends. And wouldn't you know it, I skipped a few weeks there, and suddenly Ruby and Rails have almost as many books out as Python. I counted eleven Ruby/RoR titles tonight, and thirteen for Python (including one Zope book). And Ruby had a big display section at the end of one of the shelves.
Not all the publishers were O'Reilly and Pragmatic Press. I'm pretty sure there were two or three others there, so it's not just a plot by Tim O'Reilly to sell more books. Well, actually that's exactly what it is, but it's based on actual market research that led him to the conclusion that Rails and Ruby are both gathering steam like nobody's business.
I like a lot of languages. Really, I do. But I use Ruby. I'm not even sure if I like Ruby. The issue might just be irrelevant to whether I use it. I like OCaml, for instance, but I don't use it. I don't like Java, but I do use it. Liking and using are mostly orthogonal dimensions, and if you like the language you're using even a little bit, you're lucky. That, or you just haven't gotten broad enough exposure to know how miserable you ought to be.
I use Ruby because it's been the path of least resistance for most of my programming tasks since about 3 days after I started messing with it, maybe 4 years ago.
I don't even really know Ruby all that well. I never bothered to learn it. I did read "Ruby in a Nutshell" cover-to-cover, but it's a short read (and it's a bit out of date now.) Then I read bits of "Programming Ruby", but not all of it. And now I use Ruby for everything I can, any time I have any choice in the matter. I don't even mind that I don't know the language all that well. It has a tiny core that serves me admirably well, and it's easy to look things up when you need to.
I do a lot more programming in Python than in Ruby -- Jython in my game server, and Python at work, since that's what everyone there uses for scripting. I have maybe 3x more experience with Python than with Ruby (and 10x more experience with Perl). But Perl and Python both have more unnecessary conceptual overhead, so I find I have to consult the docs more often with both of them. And when all's said and done, Ruby code generally winds up being the most direct and succinct, whether it's mine or someone else's.
I have a lot of trouble writing about Ruby, because I find there's nothing to say. It's why I almost never post to the O'Reilly Ruby blog. Ruby seems so self-explanatory to me. It makes it almost boring; you try to focus on Ruby and you wind up talking about some problem domain instead of the language. I think that's the goal of all programming languages, but so far Ruby's one of the few to succeed at it so well.
If only it performed better. *Sigh*. Well, its performance is in the same class as Perl/Python/JavaScript/Lua/Bash/etc., so there are still plenty of tasks Ruby's admirably suited for.
I think next year Ruby's going to be muscling in on Perl in terms of mindshare, or shelf-share, at B&N.
I still do most of my programming in Java -- at least half of it, maybe more. The Java platform continues to make amazing strides. The newest incarnation (JDK 6) has lots of goodies I can't wait to play with. Like Rhino, for instance, and although they appear to have gutted it, it's still awesome. I think it's the best choice they possibly could have made. Thank God they didn't bundle Groovy. What a catastrophe that was, and still is, and would have been for Java if they'd bundled it. Rhino rocks.
The JVM is just getting faster and more stable, and there are even some OK libraries that come with it. I used to think the Java platform libraries were the cat's meow. Heck, I thought they were the whole damn cat. But working with better libraries in miscellaneous other languages has got me thinking that Java's libraries are hit-or-miss.
Example: Java's concurrency libraries (java.util.concurrent[.*]) are to die for. I mean, if you're stuck with threads. I think in the fullness of time, hand-managed threads will be history, but in the meantime, Java's concurrency libraries are just superb.
I recently ported a medium-sized Python program I'd written (about 1200 lines of fairly dense Python code) to Java, because the Python was taking about an hour to run, and I wanted to parallelize the work. I spent about 3 days doing the rewrite: one day on the straight port, a day adding in the threading, and a day fine-tuning it. The straight port wound up as 1300 lines of Java (surprising that it wasn't bigger, but maybe I code in Python with a Java accent?), and ran about 50% faster, down to about 30 minutes. After adding in the threading and state machine, the program ran in 50 to 60 seconds.
So I got an order of magnitude improvement with only about a 50% increase overall in program size. The vast majority of the improvement was attributable to the threading, which in turn would have taken me FAR longer if I'd been using raw synchronization primitives. The java.util.concurrent stuff made it a snap.
On the other hand, Java's DOM implementation completely blows chunks. It quickly became the bottleneck in my application, due to an O(n) algorithm I stumbled across with no good workaround for. I can't remember exactly where it was (this was back in July), but I found a sheepishly apologetic comment from the author in the online docs. It was something to do with setting attributes on nodes while you're doing a traversal of some sort: something you'd definitely want to be fast, but it had at least linear performance, maybe worse, and now accounts for 95+% of my app's processing time.
And of course Java's DOM interface blows too, because you can't create subclasses or decorators or do anything useful with the DOM other than use it as a temp container until you've transfered the data to something more flexible.
Java's collections library is decent, but not superb. It's nice having the data structures they provide, but they're not very configurable, and the language itself makes them often cumbersome. For instance, you can have a WeakHashMap (nice), or an IdentityHashMap (nice), or a ConcurrentHashMap (also nice), but you can't combine any two of those three properties into a single hashtable. Lame.
And java.util is missing implementations and/or interfaces for a bunch of important data types like priority queues (you're stuck using a TreeSet, which is overkill), the disjoint set ADT, splay trees, bloom filters, multi-maps, and of course any kind of built-in graph support. Java hyper-enthusiasts will tell you: "well, go write your own! Or use one of the many hopefully robust implementations on the web!" That seems lame to me. We're talking about data structures here: they're more fundamental than, say, LDAP libraries and much of the other stuff Sun's bundling these days. It's smartest to provide robust, tuned implementations of these things, because it empowers average Java programmers to write faster, more reliable code.
Oh, and let's not even get me started with java.nio. What a mess! It's pretty gross, especially if you come from the comparatively simple background of select() and poll() on Unix. But maybe the grossness was necessary. I'll give them the benefit of the doubt. What bugs me isn't that the API is conceptually weird and complex (and buggy as hell last time I checked); what bugs me is that nobody at Sun bothered to put a layer atop java.nio for ordinary programmers. Like, say, a nonblocking DataInputStream that takes a type to read, a Buffer, and a callback to call when it's finished reading. So every frigging Java programmer on the planet has to write that exact class -- or just flail around with the raw APIs, which is what I think most of them do.
And look what they did to poor LDAP! I mean, the LDAP bindings are dirt-simple in every language I've ever used. It's supposed to be lightweight -- that's what the "L" stands for, fer cryin' out loud. JNDI is this huge monster. So is JMX. I mean, Java libraries have this way of being so bloated and overengineered.
But whatever; I've digressed. Java's libraries are not its biggest failing. The libraries (as I said) are decent, and the platform (in terms of tools, speed, reliability, documentation, portability, monitoring, etc.) really raises the bar on all those other loser languages out there. All of 'em. It's why no better languages have managed to supplant Java yet. Even if the language and its libraries are (on the whole) better than Java's, they also have to contend with the Java platform, and so far nobody's been able to touch it, unless maybe it's .NET, but who cares about .NET? Certainly not Amazon.com or Yahoo! or Google or any other important companies that I'm aware of.
Anyway, Java's biggest failing, I've decided, is its lack of syntax for literal data objects. It's an umbrella failing that accounts for most of the issues I have with the language.
The idea behind literals is that you have some sort of serialized notation for your data type, and it's part of the language syntax, so you can embed pre-initialized objects in your code.
The most obvious ones are numbers, booleans and strings. It's hard to imagine life without support for numeric literals, isn't it? Well, Java's support is limited at best. There's no syntax for entering a binary value, for instance, like "0b10010100". And there's no BigInteger/BigDecimal syntax, so working with them is a disaster and nobody does it if they can help it. Heck, Java doesn't even have unsigned ints and longs. But Java does more or less the bare minimum for numbers, so people don't notice it much.
Imagine if there were no String literals, so that instead of this:
you had to do this:
Not only is the latter bloated and ugly and error-prone (can you spot the error in mine?), it's also butt-slow. Literals provide the compiler with opportunities for optimization.
Well, unfortunately this OOP garbage is exactly what you have to do when you're initializing a hashtable in Java. Nearly all other languages these days have support for hashtable/hashmap literals, something like:
That's the syntax used by Python and JavaScript, but other languages are similar. The Java equivalent is this:
It might not look that much worse from this simple example, but there are definitely problems. One is optimization; the compiler is unlikely to be able to optimize all these method calls, whereas with a literal syntax, it could potentially save on method call overhead during construction of the table (and maybe other savings as well.)
Another is nested data structures. In JavaScript (and Python, Ruby, etc.) you just declare them in a nested fashion, like so:
It would be hard to do this particular one in Java 5 because of the mixed value types, though it's probably not an issue since using mixed-type data structures is something you rarely do in practice, even in dynamically-typed languages. But even if all the values were hashes of string-to-string, how are you going to do it in Java without literals? You can't. You're stuck with:
And then you find out later that your clever
Java programmers wind up dealing with this kind of thing by writing generic helper functions, and it winds up layering even more OOP overhead onto something that ought to be a simple declaration. It also tends to be brutally slow; e.g. you could write a function called buildHashMap that took an array of {key, value, key, value, ...}, but it adds a huge constant-factor overhead.
This is why Java programmers rely on XML so heavily, and it imposes both an impedance mismatch (XML is not Java, so you have to translate back and forth) and a performance penalty.
But the story doesn't end there. What about Vector/ArrayList literals? Java has primitive array literals, which is nice as far as it goes:
Unfortunately, Java's primitive arrays are a huge wart; they don't have methods, can't be subclassed, and basically fall entirely outside the supposedly beautiful OOP-land that Java has created. It was for performance, to help capture skeptical C++ programmers, and they have their place. But I don't see why they should have all the syntactic support. I mean, the [] array-indexing operator is ONLY available for Java arrays. Sure would be nice to have it for ArrayLists, wouldn't it? And Strings? And FileInputStreams?
But for some reason, Java gave arrays not one, but TWO syntactic sugarings, and then didn't give that sugar to anything else array-like in the language.
So for building ArrayLists, LinkedLists, TreeMaps and the like, you're stuck with Swing-style code assemblages.
I think of them as Swing-style because I used to do a lot of AWT and Swing programming, back when I was a Thick Client kind of guy, and they have a distinct(ly unpleasant) footprint. It looks vaguely like this, in pseudo-Swing:
Building UIs in Swing is this huge, festering gob of object instantiations and method calls. It's OOP at its absolute worst. So people have come up with minilanguages (like the TableLayout), and declarative XML replacements like Apache Jelly, and other ways to try to ease the pain.
I was on a team at Amazon many years ago that was planning to port a big internal Swing application to the web, and we were looking at the various ways to do web programming, which at the time (for Java) were pretty much limited to JSP, WebMacro, and rolling your own Swing-like HTML component library.
We experimented with the OOP approach to HTML generation and quickly discarded it as unmaintainable. (Tell that to any OOP fanatic and watch their face contort as they try to reconcile their conflicting ideas about what constitutes good programming practice.)
The right solution in this case is, of course, a Lisp dialect; Lisp really shines at this sort of thing. But Lisp isn't so hot at algebraic expressions, and the best Lisp machines no longer look so cutting-edge compared to the JVM, and blah blah blah, so people don't use Lisp. So it goes.
The next-best solutions are all about equally bad. You have your XML-language approaches (like Jelly, but for the web), but they don't give you sufficient expressiveness for control flow -- presentation logic really does require code, and it gets ugly in XML in a real hurry. You have your JSP-style templating approaches, and they aren't bad, but they can have as many as 4 or 5 different languages mixed in the same source file, which presents various problems for your tools (both the IDEs and the batch tools).
And then you have a long tail of other approaches, none of which manage to be very satisfying, but that's not really the fault of the languages. It's the browsers' fault: they START with three languages (HTML, CSS, and JavaScript), rather than having just one language to control the entire presentation, and it only goes downhill from there.
But NONE of the approaches to web templating is as bad as Swing-style programming, with a huge thicket of calls to new(), addChild(), setAttribute(), addListener(), and the like. The only approach that's worse (and even it might just be tied) is raw HTML printing:
So we're all in agreement. OOP-style assembly of parents and children is the worst way to generate HTML. You want to use declarations; you want a template, something that visually looks like the end result you're trying to create.
Well, it's the exact same situation for data structures, isn't it? You'd rather draw a picture of it (in a sense, that's exactly what you're doing with syntax for literals) than write a bunch of code to assemble it. This is all assuming that you're working with a small data set, of course. But that happens all the time in real-world programs; it's ubiquitous. So you kinda want your language to support it syntactically.
And so far we've only covered literal syntax for HashMaps and ArrayLists (which you can combine to produce various kinds of custom Trees.) Already Java's way behind other languages, and we haven't discussed any richer data types.
Like, say, objects.
JavaScript does it the best here, IMO, in the parity between hashes and objects. It's not really possible in Ruby or Python to declare a class, then create instances of the class using literal notation the way you can in JavaScript, where the keys are the names of instance variables. Fortunately you can accomplish this in either Ruby or Python with just a smidge of metaprogramming, so it's spilt milk at worst.
In Java, you only have one big hammer (instantiation), and one big wrench (the method call), so that's what you use. All you can really do to help is create a constructor that takes arguments that populate the instance variables. But if any of your instance variables are collections (other than arrays), then you're back to the old create-setprops-addchild, create-setprops-addchild pattern again.
And what about functions? Ruby and JavaScript and Lisp and Scheme and Lua and Haskell and OCaml and most other self-respecting languages have function literals. That is, they have a syntax for declaring an instance of a function as a data object in your code that you can assign to a variable, or pass as a parameter.
(Python has them too, but unfortunately they can only be one line, so Python folks prefer to pretend anonymous functions aren't very important. This is one of the 10 or so big problems caused by Python's whitespace policy. Don't ever let 'em tell you it doesn't cause problems. It does. Maybe it's worth the trade-off; that's a personal style preference, but they should at least admit the tradeoff exists.)
Well, Java sort of has them, but Java's static type system doesn't have a literal syntax for a method signature. It's pretty easy to imagine one, e.g. something like:
This imaginary syntax declares a variable
Yuck. But at least they let you do it; the alternative of not having it at all is definitely worse.
Still... isn't syntactic sugar nice? I mean, they added the "smart" for-loop, which Java programmers just rave about. So someone, somewhere in the Java community thinks syntax is good. I'm not sure many of them really understand the difference between syntactic sugar (into which category the "smart" for-loop falls) and orthogonal syntax, in which the basic operators apply to all data types for which those operators make sense, and there are literal declarations possible for every data type.
Let alone the next step, which is extensible syntax -- but that idea strikes fear into the hearts of many otherwise brave Java programmers, and Rubyists and Pythonistas as well, so let's back it up a notch to "orthogonal", and keep everyone calm.
So there you have it: Java's biggest failing. It's the literals. No literal syntax for array-lists (or linked lists or tree sets), nothing for hashtables, nothing for objects of classes you've personally defined, none for functions or function signatures. Java programmers all around the world spend a *lot* of their time working around the problem, using XML and YAML and JSON and other non-Java data-declaration languages, and writing tons of code (whole frameworks, even) for serializing and deserializing these declarations to and from Java. For the smaller stuff, they just write helper functions, which wind up being bloated, inefficient, error-prone, and extremely unsatisfying.
Java's next-biggest failing may well be the lack of orthogonality in its set of operators. We can live without operator overloading, I suppose (the simplest form of extensible syntax), but only if Sun makes operators like [] and + actually work for objects other than arrays and Strings, respectively. Jeez.
You can draw your own conclusions about why suddenly there are all these books on Ruby appearing on the bookshelves. It's a mix of truths, no doubt. And you can draw your own conclusions about why Sun's adding support for scripting languages to the JVM, rather than simply fixing Java so that people don't want (need, really) to use those other languages.
But when you dig down into a programming language, and you get past all the hype and the hooplah, what you find is a set of policies and decisions that affect your everyday life as a programmer in ways you can't ignore, and that no amount of hype will smooth over.
If your language is sitting on you like an invisible elephant, and everyone using the language is struggling to work around the same problems, then it's inevitable that other languages will come into play. Libraries can make you more productive, but they have almost no effect on the scalability of the language.
Every language has a complexity ceiling, and it's determined by a whole slew of policy and design decisions within the language, not the libraries. The slew includes the type system (with its attendant hundreds of mini-policies), and the syntax, and it also includes the language's consistency: the ratio of rules to exceptions.
Java's demonstrating quite clearly that at a certain level of complexity, the libraries and frameworks start to collapse under their own weight. People are always writing "lightweight" replacements for existing fat Java libraries and frameworks, and then the replacements get replaced, ad infinitum. But have you ever seen anyone write a replacement for XPath? Nope. It's not like everyone is rushing out to write the next big XML-querying framework. This is because XPath is a language, not a library, and it's orders of magnitude more conceptually scalable than the equivalent DOM manipulations.
Object-Oriented Programming. Touted even by skeptics as a radical leap forward in productivity, and all OOP really is boils down to a set of organizational techniques. Organization is nice, sure.
But it's pretty clear that OOP alone doesn't cut it; it has to be supplemented with Language-Oriented Programming and DSLs. And all languages, DSLs and general-purpose languages alike, have to be designed to maximize consistency; each inconsistency and special-case in the language adds to its conceptual overhead and lowers the complexity ceiling.
So you can look at the shelves filling up with Ruby books and chalk it up to marketing hype, but I have a different theory. I think it's entirely due to complexity management: Ruby does a better job of helping managing complexity than its competitors. It doesn't do a perfect job, mind you -- far from it. But it's enough of a step forward in productivity (even over Perl and Python) that it's managing to shoulder its way in to a pretty crowded language space.
With that in mind, despite my griping about Java's failings, I think Sun might actually be doing the right thing by introducing scripting languages (and improving support for them in the JVM.) Maybe. Their investment isn't really so much in Java as it is in the JVM; the JVM is their .NET. Java's not really about productivity, not really -- it's got a lot of strengths (performance, deployment, reliability, static checkability, and so on), but productivity isn't high on the list. So maybe the best way to address the productivity issue, for folks who really need it more than raw performance, is to introduce new JVM languages rather than try to pull Java in two directions.
We'll see. And with that, I think I've officially un-blocked myself; I seem to be able to blog again. So I'm declaring the Blogger's Block series finished!
Well, I held out for a week. Then I read the comments. Argh! Actually they were fine. Nice comments, all around. Whew.
I don't have any big themes to talk about today, but I've got a couple of little ones, let's call 'em bloguettes, that I'll lump together into a medley for today's entree.
Bloguette #1: Ruby Sneaks up on Python
I was in Barnes today, doing my usual weekend stroll through the tech section. Helps me keep up on the latest trends. And wouldn't you know it, I skipped a few weeks there, and suddenly Ruby and Rails have almost as many books out as Python. I counted eleven Ruby/RoR titles tonight, and thirteen for Python (including one Zope book). And Ruby had a big display section at the end of one of the shelves.
Not all the publishers were O'Reilly and Pragmatic Press. I'm pretty sure there were two or three others there, so it's not just a plot by Tim O'Reilly to sell more books. Well, actually that's exactly what it is, but it's based on actual market research that led him to the conclusion that Rails and Ruby are both gathering steam like nobody's business.
I like a lot of languages. Really, I do. But I use Ruby. I'm not even sure if I like Ruby. The issue might just be irrelevant to whether I use it. I like OCaml, for instance, but I don't use it. I don't like Java, but I do use it. Liking and using are mostly orthogonal dimensions, and if you like the language you're using even a little bit, you're lucky. That, or you just haven't gotten broad enough exposure to know how miserable you ought to be.
I use Ruby because it's been the path of least resistance for most of my programming tasks since about 3 days after I started messing with it, maybe 4 years ago.
I don't even really know Ruby all that well. I never bothered to learn it. I did read "Ruby in a Nutshell" cover-to-cover, but it's a short read (and it's a bit out of date now.) Then I read bits of "Programming Ruby", but not all of it. And now I use Ruby for everything I can, any time I have any choice in the matter. I don't even mind that I don't know the language all that well. It has a tiny core that serves me admirably well, and it's easy to look things up when you need to.
I do a lot more programming in Python than in Ruby -- Jython in my game server, and Python at work, since that's what everyone there uses for scripting. I have maybe 3x more experience with Python than with Ruby (and 10x more experience with Perl). But Perl and Python both have more unnecessary conceptual overhead, so I find I have to consult the docs more often with both of them. And when all's said and done, Ruby code generally winds up being the most direct and succinct, whether it's mine or someone else's.
I have a lot of trouble writing about Ruby, because I find there's nothing to say. It's why I almost never post to the O'Reilly Ruby blog. Ruby seems so self-explanatory to me. It makes it almost boring; you try to focus on Ruby and you wind up talking about some problem domain instead of the language. I think that's the goal of all programming languages, but so far Ruby's one of the few to succeed at it so well.
If only it performed better. *Sigh*. Well, its performance is in the same class as Perl/Python/JavaScript/Lua/Bash/etc., so there are still plenty of tasks Ruby's admirably suited for.
I think next year Ruby's going to be muscling in on Perl in terms of mindshare, or shelf-share, at B&N.
Bloguette #2: Java's Biggest Failing (Literally)
I still do most of my programming in Java -- at least half of it, maybe more. The Java platform continues to make amazing strides. The newest incarnation (JDK 6) has lots of goodies I can't wait to play with. Like Rhino, for instance, and although they appear to have gutted it, it's still awesome. I think it's the best choice they possibly could have made. Thank God they didn't bundle Groovy. What a catastrophe that was, and still is, and would have been for Java if they'd bundled it. Rhino rocks.
The JVM is just getting faster and more stable, and there are even some OK libraries that come with it. I used to think the Java platform libraries were the cat's meow. Heck, I thought they were the whole damn cat. But working with better libraries in miscellaneous other languages has got me thinking that Java's libraries are hit-or-miss.
Example: Java's concurrency libraries (java.util.concurrent[.*]) are to die for. I mean, if you're stuck with threads. I think in the fullness of time, hand-managed threads will be history, but in the meantime, Java's concurrency libraries are just superb.
I recently ported a medium-sized Python program I'd written (about 1200 lines of fairly dense Python code) to Java, because the Python was taking about an hour to run, and I wanted to parallelize the work. I spent about 3 days doing the rewrite: one day on the straight port, a day adding in the threading, and a day fine-tuning it. The straight port wound up as 1300 lines of Java (surprising that it wasn't bigger, but maybe I code in Python with a Java accent?), and ran about 50% faster, down to about 30 minutes. After adding in the threading and state machine, the program ran in 50 to 60 seconds.
So I got an order of magnitude improvement with only about a 50% increase overall in program size. The vast majority of the improvement was attributable to the threading, which in turn would have taken me FAR longer if I'd been using raw synchronization primitives. The java.util.concurrent stuff made it a snap.
On the other hand, Java's DOM implementation completely blows chunks. It quickly became the bottleneck in my application, due to an O(n) algorithm I stumbled across with no good workaround for. I can't remember exactly where it was (this was back in July), but I found a sheepishly apologetic comment from the author in the online docs. It was something to do with setting attributes on nodes while you're doing a traversal of some sort: something you'd definitely want to be fast, but it had at least linear performance, maybe worse, and now accounts for 95+% of my app's processing time.
And of course Java's DOM interface blows too, because you can't create subclasses or decorators or do anything useful with the DOM other than use it as a temp container until you've transfered the data to something more flexible.
Java's collections library is decent, but not superb. It's nice having the data structures they provide, but they're not very configurable, and the language itself makes them often cumbersome. For instance, you can have a WeakHashMap (nice), or an IdentityHashMap (nice), or a ConcurrentHashMap (also nice), but you can't combine any two of those three properties into a single hashtable. Lame.
And java.util is missing implementations and/or interfaces for a bunch of important data types like priority queues (you're stuck using a TreeSet, which is overkill), the disjoint set ADT, splay trees, bloom filters, multi-maps, and of course any kind of built-in graph support. Java hyper-enthusiasts will tell you: "well, go write your own! Or use one of the many hopefully robust implementations on the web!" That seems lame to me. We're talking about data structures here: they're more fundamental than, say, LDAP libraries and much of the other stuff Sun's bundling these days. It's smartest to provide robust, tuned implementations of these things, because it empowers average Java programmers to write faster, more reliable code.
Oh, and let's not even get me started with java.nio. What a mess! It's pretty gross, especially if you come from the comparatively simple background of select() and poll() on Unix. But maybe the grossness was necessary. I'll give them the benefit of the doubt. What bugs me isn't that the API is conceptually weird and complex (and buggy as hell last time I checked); what bugs me is that nobody at Sun bothered to put a layer atop java.nio for ordinary programmers. Like, say, a nonblocking DataInputStream that takes a type to read, a Buffer, and a callback to call when it's finished reading. So every frigging Java programmer on the planet has to write that exact class -- or just flail around with the raw APIs, which is what I think most of them do.
And look what they did to poor LDAP! I mean, the LDAP bindings are dirt-simple in every language I've ever used. It's supposed to be lightweight -- that's what the "L" stands for, fer cryin' out loud. JNDI is this huge monster. So is JMX. I mean, Java libraries have this way of being so bloated and overengineered.
But whatever; I've digressed. Java's libraries are not its biggest failing. The libraries (as I said) are decent, and the platform (in terms of tools, speed, reliability, documentation, portability, monitoring, etc.) really raises the bar on all those other loser languages out there. All of 'em. It's why no better languages have managed to supplant Java yet. Even if the language and its libraries are (on the whole) better than Java's, they also have to contend with the Java platform, and so far nobody's been able to touch it, unless maybe it's .NET, but who cares about .NET? Certainly not Amazon.com or Yahoo! or Google or any other important companies that I'm aware of.
Literals
Anyway, Java's biggest failing, I've decided, is its lack of syntax for literal data objects. It's an umbrella failing that accounts for most of the issues I have with the language.
The idea behind literals is that you have some sort of serialized notation for your data type, and it's part of the language syntax, so you can embed pre-initialized objects in your code.
The most obvious ones are numbers, booleans and strings. It's hard to imagine life without support for numeric literals, isn't it? Well, Java's support is limited at best. There's no syntax for entering a binary value, for instance, like "0b10010100". And there's no BigInteger/BigDecimal syntax, so working with them is a disaster and nobody does it if they can help it. Heck, Java doesn't even have unsigned ints and longs. But Java does more or less the bare minimum for numbers, so people don't notice it much.
Imagine if there were no String literals, so that instead of this:
String s = "Hello, world!";
you had to do this:
StringBuffer sb = new StringBuffer();
sb.append('H');
sb.append('e');
sb.append('l');
sb.append('l');
sb.append('o');
sb.append(',');
sb.append(' ');
sb.append('W').append('o').append('r').append('l').append('d').append('!');
String s = sb.toString();
Not only is the latter bloated and ugly and error-prone (can you spot the error in mine?), it's also butt-slow. Literals provide the compiler with opportunities for optimization.
Well, unfortunately this OOP garbage is exactly what you have to do when you're initializing a hashtable in Java. Nearly all other languages these days have support for hashtable/hashmap literals, something like:
my_hashmap = {
"key1" : "value1",
"key2" : "value2",
"key3" : "value3",
...
}
That's the syntax used by Python and JavaScript, but other languages are similar. The Java equivalent is this:
Map<String, String> my_hashmap = new HashMap<String, String>();
my_hashmap.put("key1", "value1");
my_hashmap.put("key2", "value2");
my_hashmap.put("key3", "value3");
...
It might not look that much worse from this simple example, but there are definitely problems. One is optimization; the compiler is unlikely to be able to optimize all these method calls, whereas with a literal syntax, it could potentially save on method call overhead during construction of the table (and maybe other savings as well.)
Another is nested data structures. In JavaScript (and Python, Ruby, etc.) you just declare them in a nested fashion, like so:
my_thingy = {
"key1": { "foo": "bar", "foo2": "bar2"},
"key2": ["this", "is", "a", "literal", "array"],
"key3": 37.5,
"key4": "Hello, world!",
...
}
It would be hard to do this particular one in Java 5 because of the mixed value types, though it's probably not an issue since using mixed-type data structures is something you rarely do in practice, even in dynamically-typed languages. But even if all the values were hashes of string-to-string, how are you going to do it in Java without literals? You can't. You're stuck with:
Map<String, Map<String, String>> my_hashmap = \
new HashMap<String, HashMap<String, String>>();
Map<String, String> value = new HashMap<String, String>();
value.put("foo", "bar");
value.put("foo2", "bar2");
my_hashmap.put("key1, value);
value.clear();
value.put("foo3", "bar3");
value.put("foo4", "bar4");
my_hashmap.put("key2, value);
...
And then you find out later that your clever
clear()
optimization (instead of creating a new HashMap object for each value) busted it completely. Whee.Java programmers wind up dealing with this kind of thing by writing generic helper functions, and it winds up layering even more OOP overhead onto something that ought to be a simple declaration. It also tends to be brutally slow; e.g. you could write a function called buildHashMap that took an array of {key, value, key, value, ...}, but it adds a huge constant-factor overhead.
This is why Java programmers rely on XML so heavily, and it imposes both an impedance mismatch (XML is not Java, so you have to translate back and forth) and a performance penalty.
But the story doesn't end there. What about Vector/ArrayList literals? Java has primitive array literals, which is nice as far as it goes:
String[] s = new String[]{"fee", "fi", "fo", "fum"};
Unfortunately, Java's primitive arrays are a huge wart; they don't have methods, can't be subclassed, and basically fall entirely outside the supposedly beautiful OOP-land that Java has created. It was for performance, to help capture skeptical C++ programmers, and they have their place. But I don't see why they should have all the syntactic support. I mean, the [] array-indexing operator is ONLY available for Java arrays. Sure would be nice to have it for ArrayLists, wouldn't it? And Strings? And FileInputStreams?
But for some reason, Java gave arrays not one, but TWO syntactic sugarings, and then didn't give that sugar to anything else array-like in the language.
So for building ArrayLists, LinkedLists, TreeMaps and the like, you're stuck with Swing-style code assemblages.
I think of them as Swing-style because I used to do a lot of AWT and Swing programming, back when I was a Thick Client kind of guy, and they have a distinct(ly unpleasant) footprint. It looks vaguely like this, in pseudo-Swing:
Panel p = new Panel(new FlowLayout());
JButton b = new JButton("Press me!");
b.setEventListener(somethingOrOther);
p.add(b);
JSomething foo = new JSomething(blah, blah);
foo.setAttribute();
foo.setOtherAttribute();
foo.soGladIDontDoThisKindOfThingAnymore();
p.add(foo);
...
Building UIs in Swing is this huge, festering gob of object instantiations and method calls. It's OOP at its absolute worst. So people have come up with minilanguages (like the TableLayout), and declarative XML replacements like Apache Jelly, and other ways to try to ease the pain.
I was on a team at Amazon many years ago that was planning to port a big internal Swing application to the web, and we were looking at the various ways to do web programming, which at the time (for Java) were pretty much limited to JSP, WebMacro, and rolling your own Swing-like HTML component library.
We experimented with the OOP approach to HTML generation and quickly discarded it as unmaintainable. (Tell that to any OOP fanatic and watch their face contort as they try to reconcile their conflicting ideas about what constitutes good programming practice.)
The right solution in this case is, of course, a Lisp dialect; Lisp really shines at this sort of thing. But Lisp isn't so hot at algebraic expressions, and the best Lisp machines no longer look so cutting-edge compared to the JVM, and blah blah blah, so people don't use Lisp. So it goes.
The next-best solutions are all about equally bad. You have your XML-language approaches (like Jelly, but for the web), but they don't give you sufficient expressiveness for control flow -- presentation logic really does require code, and it gets ugly in XML in a real hurry. You have your JSP-style templating approaches, and they aren't bad, but they can have as many as 4 or 5 different languages mixed in the same source file, which presents various problems for your tools (both the IDEs and the batch tools).
And then you have a long tail of other approaches, none of which manage to be very satisfying, but that's not really the fault of the languages. It's the browsers' fault: they START with three languages (HTML, CSS, and JavaScript), rather than having just one language to control the entire presentation, and it only goes downhill from there.
But NONE of the approaches to web templating is as bad as Swing-style programming, with a huge thicket of calls to new(), addChild(), setAttribute(), addListener(), and the like. The only approach that's worse (and even it might just be tied) is raw HTML printing:
print("<html><body>...</body></html>");
So we're all in agreement. OOP-style assembly of parents and children is the worst way to generate HTML. You want to use declarations; you want a template, something that visually looks like the end result you're trying to create.
Well, it's the exact same situation for data structures, isn't it? You'd rather draw a picture of it (in a sense, that's exactly what you're doing with syntax for literals) than write a bunch of code to assemble it. This is all assuming that you're working with a small data set, of course. But that happens all the time in real-world programs; it's ubiquitous. So you kinda want your language to support it syntactically.
And so far we've only covered literal syntax for HashMaps and ArrayLists (which you can combine to produce various kinds of custom Trees.) Already Java's way behind other languages, and we haven't discussed any richer data types.
Like, say, objects.
JavaScript does it the best here, IMO, in the parity between hashes and objects. It's not really possible in Ruby or Python to declare a class, then create instances of the class using literal notation the way you can in JavaScript, where the keys are the names of instance variables. Fortunately you can accomplish this in either Ruby or Python with just a smidge of metaprogramming, so it's spilt milk at worst.
In Java, you only have one big hammer (instantiation), and one big wrench (the method call), so that's what you use. All you can really do to help is create a constructor that takes arguments that populate the instance variables. But if any of your instance variables are collections (other than arrays), then you're back to the old create-setprops-addchild, create-setprops-addchild pattern again.
And what about functions? Ruby and JavaScript and Lisp and Scheme and Lua and Haskell and OCaml and most other self-respecting languages have function literals. That is, they have a syntax for declaring an instance of a function as a data object in your code that you can assign to a variable, or pass as a parameter.
(Python has them too, but unfortunately they can only be one line, so Python folks prefer to pretend anonymous functions aren't very important. This is one of the 10 or so big problems caused by Python's whitespace policy. Don't ever let 'em tell you it doesn't cause problems. It does. Maybe it's worth the trade-off; that's a personal style preference, but they should at least admit the tradeoff exists.)
Well, Java sort of has them, but Java's static type system doesn't have a literal syntax for a method signature. It's pretty easy to imagine one, e.g. something like:
(int, int) -> String x;
This imaginary syntax declares a variable
x
that takes 2 ints as parameters and returns a string. Lots of languages have signature-syntax of some sort, and Java's syntax space is definitely sparse enough that they could pick a good syntax for it without fear of collisions, even conceptual collisions. But no such luck. Instead, when you want to do this sort of thing you have to declare a named interface, and then inside of it declare at least one named method (which is where the params and return type show up), and then you're still not done, because when you create the function you have to create an anonymous (or named) class that contains the definition of the function that matches the interface.Yuck. But at least they let you do it; the alternative of not having it at all is definitely worse.
Still... isn't syntactic sugar nice? I mean, they added the "smart" for-loop, which Java programmers just rave about. So someone, somewhere in the Java community thinks syntax is good. I'm not sure many of them really understand the difference between syntactic sugar (into which category the "smart" for-loop falls) and orthogonal syntax, in which the basic operators apply to all data types for which those operators make sense, and there are literal declarations possible for every data type.
Let alone the next step, which is extensible syntax -- but that idea strikes fear into the hearts of many otherwise brave Java programmers, and Rubyists and Pythonistas as well, so let's back it up a notch to "orthogonal", and keep everyone calm.
So there you have it: Java's biggest failing. It's the literals. No literal syntax for array-lists (or linked lists or tree sets), nothing for hashtables, nothing for objects of classes you've personally defined, none for functions or function signatures. Java programmers all around the world spend a *lot* of their time working around the problem, using XML and YAML and JSON and other non-Java data-declaration languages, and writing tons of code (whole frameworks, even) for serializing and deserializing these declarations to and from Java. For the smaller stuff, they just write helper functions, which wind up being bloated, inefficient, error-prone, and extremely unsatisfying.
Java's next-biggest failing may well be the lack of orthogonality in its set of operators. We can live without operator overloading, I suppose (the simplest form of extensible syntax), but only if Sun makes operators like [] and + actually work for objects other than arrays and Strings, respectively. Jeez.
Epiblogue
You can draw your own conclusions about why suddenly there are all these books on Ruby appearing on the bookshelves. It's a mix of truths, no doubt. And you can draw your own conclusions about why Sun's adding support for scripting languages to the JVM, rather than simply fixing Java so that people don't want (need, really) to use those other languages.
But when you dig down into a programming language, and you get past all the hype and the hooplah, what you find is a set of policies and decisions that affect your everyday life as a programmer in ways you can't ignore, and that no amount of hype will smooth over.
If your language is sitting on you like an invisible elephant, and everyone using the language is struggling to work around the same problems, then it's inevitable that other languages will come into play. Libraries can make you more productive, but they have almost no effect on the scalability of the language.
Every language has a complexity ceiling, and it's determined by a whole slew of policy and design decisions within the language, not the libraries. The slew includes the type system (with its attendant hundreds of mini-policies), and the syntax, and it also includes the language's consistency: the ratio of rules to exceptions.
Java's demonstrating quite clearly that at a certain level of complexity, the libraries and frameworks start to collapse under their own weight. People are always writing "lightweight" replacements for existing fat Java libraries and frameworks, and then the replacements get replaced, ad infinitum. But have you ever seen anyone write a replacement for XPath? Nope. It's not like everyone is rushing out to write the next big XML-querying framework. This is because XPath is a language, not a library, and it's orders of magnitude more conceptually scalable than the equivalent DOM manipulations.
Object-Oriented Programming. Touted even by skeptics as a radical leap forward in productivity, and all OOP really is boils down to a set of organizational techniques. Organization is nice, sure.
But it's pretty clear that OOP alone doesn't cut it; it has to be supplemented with Language-Oriented Programming and DSLs. And all languages, DSLs and general-purpose languages alike, have to be designed to maximize consistency; each inconsistency and special-case in the language adds to its conceptual overhead and lowers the complexity ceiling.
So you can look at the shelves filling up with Ruby books and chalk it up to marketing hype, but I have a different theory. I think it's entirely due to complexity management: Ruby does a better job of helping managing complexity than its competitors. It doesn't do a perfect job, mind you -- far from it. But it's enough of a step forward in productivity (even over Perl and Python) that it's managing to shoulder its way in to a pretty crowded language space.
With that in mind, despite my griping about Java's failings, I think Sun might actually be doing the right thing by introducing scripting languages (and improving support for them in the JVM.) Maybe. Their investment isn't really so much in Java as it is in the JVM; the JVM is their .NET. Java's not really about productivity, not really -- it's got a lot of strengths (performance, deployment, reliability, static checkability, and so on), but productivity isn't high on the list. So maybe the best way to address the productivity issue, for folks who really need it more than raw performance, is to introduce new JVM languages rather than try to pull Java in two directions.
We'll see. And with that, I think I've officially un-blocked myself; I seem to be able to blog again. So I'm declaring the Blogger's Block series finished!
BloggersBlock block = new BloggersBlock();
block.setFinished(true);
block.tieOffAndStuff();
blog.addChild(block);
...
32 Comments:
Regarding first-class Java functions, have a look at http://gafter.blogspot.com/2006/08/closures-for-java.html .
Even though it sounds like you'll never get a chance to use it, C# is actually picking up some of the things you've wished for. For one, you can do the anonymous method thing easily now. Furthermore, C# 3 (coming next year?) gives you a better syntax for that (lambdas), extension methods (kinda like degenerate mixins if you squint), and better object initialization. You don't get the hash stuff, but you can easily create new objects with their properties set to specified values.
Something like:
Customer c = new {Name = "Rob", State="Texas"};
Better type inference is coming too so there's less Customer c = new Customer crap all over the place.
Python has them too, but unfortunately they can only be one line, so Python folks prefer to pretend anonymous functions aren't very important.
I prefer to pretend that the "anonymous" part of "anonymous functions" isn't very important. Yeah, there's no good way to directly pass a function literal. But you can define the function with a name and pass the name, and it works just about as well. It is a tradeoff, as you say, but I don't think it's as big of a tradeoff as you make it seem, in exchange for the good structure provided by the whitespace.
This is one of the 10 or so big problems caused by Python's whitespace policy.
Could you list them? Because I keep seeing people talking about the "big problems" the whitespace policy causes, but I can never find one that actually impacts me. Could be I'm programming in the wrong fields or haven't been programming long enough, but I'd still like to see what they are.
tassos: Thanks for the link. Looking forward to it. I think they actually have some great stuff up their sleeves for Dolphin and beyond.
If this makes it through, Java will get a lot of new steam. Tons. Oodles. It would be huge.
Then all they need is literals. Collection literals, yes, but also BigNumber literals, regexp literals, multi-line strings with variable substitution, at the very least.
Oh, and indexing and concatenation operators for collections, and maybe a limited form of continuations, and we'll finally be getting pretty close to a good language.
Combine that with a great platform, and I can probably declare my language-ranting closed for a while. I'm guessing it'll all come together by around JDK 9.
I wrote more about language expressability in The Ex Factor the other day.
(I also think the Ruby book sales statistic is very misleading on a quarter-by-quarter basis; most of the analyses I've seen fail to take into account the relative freshness of books in the channel, the number of books sold-through historically on a subject, and the publication ates. Those are all important factors.)
>>Tassos said...
>>
>> Regarding first-class Java
>> functions, have a look at http:/
>> /gafter.blogspot.com/2006/08/
>> closures-for-java.html
Well, of course in any Turning-complete language you can accomplish basically anything you want to do (namely, implement a Lisp interpreter in Java), but why not save yourself trouble now (and in the future when you're fixing bugs and trying to figure out what's going on) by using a respectable language?
Please disregard the previous comment; I have made a misinterpretation.
Until I read this article, I thought Java and C# were relatively (*very*) similar. I haven't worked in Java since 2000, so I just assumed the stuff I saw in C# was in Java too. For example, C# has limited support for operator overloading ([],+,etc.). In .NET v2.0 C# picked up generators, anonymous methods, generics and partial classes. [Side Note: I believe partial classes to be a big win because you can code generate part of a class and then custom write the other part in another file. The result being that you can regenerate the generated file without hurting your custom code.]
In terms of your literals discussion, I completely agree with you, and it's obvious you have been programming in Ruby lately, since it has syntactic sugar for everything that you mentioned. I would throw in that they should also steal regular expression syntax from Ruby/Perl/Python as well. Having spent about six months working on the side in Ruby, I understand your frustration. I feel the same way about C# when I have to go back to it from Ruby.
The ol' "Ruby's sellin' a lot of books" argument could be dangerous.
I mean, I never had to buy a book to learn Python. ;)
/troll.
Just kidding, but I'd recommend watching out for those oversimplifications.
Out of curiosity Mr Yegge, when you were porting your Python to Java to make it run faster, what was Python's threading lacking?
I'm not too au fait with what Java offers in that department, so I'm having trouble following your train of thought.
Also - a query on lambdas in Ruby, does it offer anything like Lisp's labels?
The invisible element with ruby and python though is speed (lack of). You touched on it with your example of converting an app from python to java.
It means Sun adding a scripting language isn't enough. We want to have our cake (ruby) and eat it too (fast, static type inference). It would be nice to see a language like boo or C# 3.0 for the jvm. Scala and Nice are the closest candidates I believe.
And I agree with code monkey, some of what you said sounds like C# 3.0. That's one language I don't think you've tried out much (also vb.net), not that you need to or should.
But they for example are adding support for xml literals in the language (javascript too i believe, E4X). That doesn't make much sense to me. I also think XPATH is a very clear and succinct syntax for navigating structured data like xml, and would like to see better xpath integration in a language.
I should mention too I did see a paper on type inference for javascript. Maybe that's a direction Sun should head now that it has adopted Rhino.
Hi Steve,
All good stuff, but to be honest, the only time where I hardcode literals inside maps or lists is when I'm setting up tests with dummy values. For production code, I pretty much never need it.
My number one feature request for Java is still a concise notation for accesssors, because even now, I use these all over the place (#2 is closures, of course, but we're working on that).
--
Cedric
What's even worse is that Java array initializations are really the same old Swing-style code under the table. When I wrote a large initialized static array in my early days of writing Java, the code in the hidden "<clinit>" method had one call for each element! I rewrote the thing to be a big String instead of a big Array, and the class shrank to a reasonable size.
cynos, the problem with Python threading (and Ruby, and most flavors of Scheme) is that it's what Java folks call "green threads". Each thread is much more lightweight on a single-processor machine, but the program as a whole can't take advantage of multiple processors at all. This problem is going to come up and bite the dynamic languages in the butt Real Soon Now.
"Not only is the latter bloated and ugly and error-prone (can you spot the error in mine?), it's also butt-slow."
No, I can't! Is something wrong with me? I even coded it and it works fine. What am I missing?
I don´t see why a tab based language can´t have anounimous methods. Boo (boo.codehaus.org) has a tab based style like python, and has closures that can take more than one line, and literals for hashs, arrays, all that been a static language, but with type inference. I think that the greatest problem with java is the idea that only OOP can be used, all need to be a object, in python it don´t need to be this way, and in ruby, all methods live in objects, but it don´t force the concept.
An interesting thing is that many of those features are coming to C# 3, (and are already in Boo, and of advertising ;) )
The Ruby book surge is not because of Ruby's language features, it's because they finally got a killer app: Rails. Without RoR you wouldn't see 80% of those books.
Some kludgy solutions you can use to initialize a list, using varargs:
List foo = Arrays.asList("1", "2", "3", "4"); // using varargs
// Lame, but works for strings
Properties foo = new Properties();
InputStream is = getClass().getResourceAsStream("hash.properties"
foo.load(is);
I know there are others...
amen on the need for literals for common data structures like hashmaps. sigh, i still miss NeXT's property-list (plist) syntax -- a version of which is still in use at Amazon... and, of course, still survives on NeXT Step, I mean OS-X.
the "error" is the 'w' was capitalized as 'W'. (Or I think that was it.)
Regarding your comments on java.nio, I absolutely agree with you that the API is gross. The problem is that java.nio implements the Reactor pattern from Pattern-Oriented Sofware Architecture vol 2 by Douglas Schmidt et al. without the possibilty of using select in a way you would do it on UNIX.
About your complaints about a layer on top of java.nio, I actually think you can almost do the task you described, it is just burried in the grossness of the API. I vaguely remember doing stuff similar to what you described with the DataInputStream when I wrapped my head around the API (I might remember wrong, though) and all the abstract classes etc (java.nio beats every creational pattern on steroids: "how the .... do I get an instance of this class grrr"). Java.nio is close to incomprehensible if your only source is the Javadoc.
A small note on missing data structures in the Java lib: there has actually been a java.util.PriorityQueue implementation since 5.0.
I've noticed you are beginning to mention the relatively unknown language Lua. Has anything special about Lua caught your attention? I think the language and its implementation is great, but I've only used it for embedding into C programs, which makes low level coding in C tolerable because you don't actually have to code in C.
Just to re-iterate what some other's have said, it really is worth investigating .NET. The fact that Unix based companies don't use it is a bit of a self-fulfilling prophecy. (Yeah, Windows has a way to go before it can be an acceptable platform for Google, but it does alright for Dell.)
My own take on the JVM changes is that basically Microsoft have forced them into it. Lots of languages seem to target .NET, because the VM developers listen and have a high quality product.
Other features worth mentioning:
there's no hashtable initialisation, but anonymous classes in C#3 are well worth looking at. There's an expression model similar to LISP's. Co-routining has been there since C#2. It's actually usable, too...
On the other hand, I'm coming to the unpleasant conclusion the ASP.NET is actually an anti-productivity tool.
Why isn't that
blog.addChild(
new BloggersBlock()
.setFinished(true)
.tieOffAndStuff());
(except for API stupidity)?
By the way, I even experienced the comment dread on my previous comment. Took me a week to check if there was any followup...
holy smack you write a lot
Steve,
>It's the browsers' fault: they START with three languages (HTML, CSS, and JavaScript), rather than having just one language to control the entire presentation, and it only goes downhill from there.
Since this is maybe the third time you have brought this up in your blog, maybe it is worth an action.
1. Come up with new language specification that satisfies the above.
2. Update firefox or some other open source browser to support it.
Probably non-trivial, but might be worthwhile for the potential impact and others might jump in to help.
Amit C
Just to re-reiterate. Try c#.
I tend to make a similar comment to linux-only freaks. If you dont take a look around, you may miss out on good things.
it's unfortunate mono isn't farther along.
> Thank God they didn't bundle Groovy. What a catastrophe that was, and still is, and would have been for Java if they'd bundled it.
Why not? I've been monitoring Groovy's mailing list and have been playing with it for a while. It seems like supurb language, especially for JVM (to compare with 'just ports' like JRuby or Jython). Any particular reason you think Groovy is so bad?
vlad: Try to 'google' for "jvm shootout groovy" and select the cached archive as the site is currently unavailable. There you'll find your answer...
Another (fairly dirty way) to have smaller initialization blocks for complex data-types is using an anon. inner class with initializer. So the list [1,2] can be initialized as:
List<int> list = new ArrayList<int>() {{ add(1); add(2);}};
We have a literate testing framework which is essentially a DSL embedded into Java for web app testing, do you have any thoughts on it?
I have a blog that has rapidshare links and tutorials on various topics like ERP, programming languages, networking and ecrtifications etc.. Would you like to exchange links
You can checkout my blog
Please visit
http://arbizaa-softwareworld.blogspot.com
One of the most important aspects of acne prevention is to keep the skin clean at all times or as much as possible. Regular bathing is essential. While bathing, be sure to scrub the skin until it is well exfoliated. Be careful not to scrub too hard, as this may irritate and dry out the skin, leading to more outbreaks. You may be better off getting a gently exfoliating product, such as Proactiv skin care when washing to prevent over exfoliation. The skin must remain as dry as possible and not be exposed to excessive perspiration. If you participate in strenuous activities, you will want to remove a sweat soaked shirt after you are done. There is also the factor of what clothes you wear in regard to acne outbreaks. Tight clothing or carrying heavy materials such as backpacks will chafe and irritate the pores and lead to flare-ups by rubbing across the hair follicles. It is advisable to alternate tight fitting clothes with loose fitting ones occasionally and to try alternating backpacks with side packs or handle bags.
This comment has been removed by the author.
Acne is a systemic inflammatory disease affecting the skin. Current statistics estimate that it is endured by 90% of adolescents, almost 50% of all adult women, and 25% of all adults, either chronically, or at some point during their lives. Acne is the most common skin affliction in the world, yet quality acne treatment is still a mystery to many sufferers. Although there is no absolute cure for acne, the treatment options available in today world make it a far more manageable condition than ever before.
Check for more Acne treatment information and products.
http://www.acne-treatment.net
The Google Collections API has some of the extended collections you're interested in.
<< Home