Summary
Developer productivity is as much a factor of productive frameworks as it is of language capabilities. Is there anything in Java that limits framework architects in their quest to design more productive APIs and frameworks? How do language features impact framework design?
Advertisement
Much recent discussion about Java has focused either on the shortcomings of the language, or on how adding new language features to remedy those shortcomings would make Java more complex, especially for less advanced programmers. While recent Java criticism appears to center around the language, the real issue is developer productivity. Bill Venners, for example, bemoans that:
For several years I have been jealous of the joy people seem to feel when they program in Python or Ruby. I believe the sense of satisfaction stems primarily from feeling, and actually being, more productive in these languages. Although I have no doubt that my choice to use Java for Artima's architecture was the right one, for me Java was a practical choice, not a heart choice. When I work in Java, I wouldn't say I feel joy. I don't feel like I'm riding a fast stallion through beautiful wooded hills, the wind in my long, flowing hair. I feel more like I'm struggling to convince a workhorse to pull a plow.
I think there is a natural law of languages that [says] that [a language] over time becomes harder and harder to enhance while still maintaining backwards compatibility. If a language is as successful as Java, it's really expensive to break source compatibility with Java in subsequent releases of the language. It becomes harder and harder to improve things... And it comes a time when it may make sense to jump to a language that is not source compatible, but is binary compatible.
If Java is to be saved at all, it needs to become like C; a workhorse that you can rely upon. In fact, any future changes to the language need to be things that simplify and clarify the language and its use (say, fixing the classpath problem), and flesh out (for example) incomplete libraries that have languished...
But we need to become especially conservative when considering major, fundamental language features like closures which, while they can be very appealing in theory, may have a cost that is too great in practice when they are forced into a language that values backward compatibility over the clarity of its abstractions.
While the capabilities and design of a language are important, a language is but one determinant of a developer's overall productivity (and I presume that a highly productive developer is a happy one, all things being equal). Many projects choose Java more for its APIs and developer and deployment tools than for its language features. Indeed, few Java developers embark these days on a new project without relying on some framework, whether for persistence, the Web tier, or even for desktop applications. Using Java is increasingly a task of interacting with Java frameworks.
If Java developers spend a large portion of their time working with frameworks, and if the best Java developers still feel lacking in productivity—"struggling to convince a workhorse to pull a plow"—to what degree is that due to the lack of agility of Java frameworks, and to what extent is that attributable to the language itself? More interestingly, to what degree does Java's design handicap framework architects to create more productive tools?
Language Power, Framework Beauty
Some of those critiquing Java often point to Ruby as an example of a productive language. Ruby code, indeed, is beautiful to look at, and I can say from experience that writing Ruby applications does make you feel productive (and sometimes happy). However, the pre-eminent Ruby application today is Rails, whereas the Java landscape is much more divided between a myriad of Web, persistence, and desktop frameworks. Could it be that part of the productivity emanating from the Ruby community is really due to Rails, and not as much to Ruby?
A little-discussed aspect of Rails is that you don't need to know much Ruby to write even a fairly substantial Rails application. A competent Java developer could learn probably in a single day all the Ruby he needed to develop a feature-rich Rails application. A typical Rails application consists of short snippets of Ruby code pulled together by the framework into a coherent whole. Behind the few lines of Ruby application of code is a clever exploiting of Ruby's metaobject protocol and module system that hides a lot of complexity from the developer.
Bruce Eckel noted that developing in Flex can also make for happy developers—happier certainly, than those trying to cobble together complex HTML pages or Swing UIs. Behind Flex, however, is the ActionScript 3 language, a version of the emerging EcmaScript 4 standard, that combines some of the ugliest aspects of Java syntax with the quirkier corners of JavaScript. Since ActionScript 3 attempts to mix together in a single stew dynamic and static typing, as well as functional and object-oriented programming techniques, the combination can potentially yield mind-boggling complexity.
But the designers of Flex hide much of that complexity: as with Rails, Flex applications tend to consist of small snippets of ActionScript code, short functions, mixed together with an XML-based UI layout language. Learning Flex is not so much a question of learning advanced features of ActionScript 3, but is about learning how the Flex framework affects some desired functionality.
Both Flex and Rails selected aspects of their respective languages that are relatively easy to master, and strongly encourage the use of those features in their designs. That's exactly what every framework should do. Without Rails nudging developers in a certain direction, steering clear of Ruby's more esoteric features, we may all be complaining now how Ruby leads to overly complex and hard-to-understand code. And without Flex, few would be inclined to touch ActionScript (or even EcmaScript 4).
In a recent blog post, Cay Horstmann complains that one his Java pain points is exactly in the area of Java frameworks:
When I embark on another web application ... I groan. I use JSF in the forlorn hope that someone else has given me components that make me not worry about AJAX and GET vs. POST, all of which have nothing to do with the problem that I want to solve. But then I ... despair. If I have to paste random pieces of code to make things work, I just know I'll be hosed when things don't work.
Come on, folks, When I write a web application, I want to be able to say "This is the common layout of my pages, give me a standard login screen, I want a menu here, and fill that table with those database values, but add a column with buttons..." I can do it in JSF, but it is like eating soup with a fork.
Why is it that Sun can't give me a decent web framework? Is it a shortcoming of the Java language? Or crummy API design? ... I think in this case, the Java language is sufficiently powerful, but we are hobbled by backwards compatibility with JSP and JSF, both of which are simultaneously complex and underpowered. Or could a language enhancement such as continuations make web programming dramatically easier? If so, why does Rails get all the buzz and not Seaside?
Horstmann asks the excellent question of whether there is something in Java that prevents framework architects to design a highly productive, cruft-free, and beloved framework in Java. If so, then what in Java makes it hard to write good frameworks in that language?
Scalable Language
On the surface, it seems that some of the emerging and popular frameworks use dynamically typed languages in order to avoid unnecessary code generation and clutter. Ruby and ActionScript 3 also provide functional features and closures that their respective frameworks fully exploit. Yet, I don't believe that any those features make these languages more suitable for framework construction than Java.
Rather, the notion of a scalable language may have to do with framework bliss to a greater extent. In their book, Programming Scala, published by Artima and recently released in PDF PrePrint, Martin Odersky, Lex Spoon, and Bill Venners, write that:
Scala has a set of convenient constructs that help you get started
quickly and let you program in a pleasantly concise style. At the same time,
you have the assurance that you will not "outgrow" the language. You can
always tailor the the program to your requirements, because everything is
based on library modules that you can select and adapt as needed...
Scala is much more like a bazaar than a cathedral, in the sense that it is designed to be extended and adapted by the people programming in it. Instead of providing all constructs you might ever need in one “perfectly complete” language, Scala puts the
tools for building such constructs into your hands.
I recall James Gosling mentioning many years ago that one of his, and Sun's, goals with Java was to create a language that allowed developers to write big programs reliably. Indeed, Java is eminently suitable for that task, as many large enterprise applications, IDEs, an application servers written in Java demonstrate.
But I have a feeling that most developers don't really want to write big programs. I certainly prefer a model of programming when I can write a few lines of code to affect just the functionality I desire. I can get that with Ruby and ActionScript, and even in Scala. But Java somehow always feels like having to drive a big rig to the convenience store. It scales up very well, but doesn't scale down as neatly for smaller jobs.
On that note though, the more I learn about Ruby, I'm beginning to think that just as Java doesn't easily scale down to smaller tasks, Ruby wouldn't scale up to large systems: The way Ruby classes can be opened up and parts of a class redefined—a very handy feature when writing Rails modules, for instance—can, for instance, lead to completely incomprehensible programs in the wrong hands.
In the case of Ruby, the existence of Rails remedies the language's potential scalability issues. As Rails is increasingly employed in larger, enterprise tasks, it remains to be seen whether the ability to scale down a language is overall more important than for a language to scale up to huge programs: It maybe that scaling up can be done with good frameworks, but scaling down really needs support in the language itself. The jury on that is still out.
Perhaps new languages that can scale down to small tasks as well as scale up to big programs, such as Scala, will lead to more powerful and better frameworks overtime.
If you were to write your own Web/persistence/desktop framework, what language would you pick? In what way do you think language impacts framework design?
To my mind, Java frameworks often end up looking like the Pompidou Centre; all the plumbing is on the outside. The typical java framework is functionally excellent, but requires a thorough understanding of the full API before it can be used, even for the 80% of cases where sensible defaults are OK. Though one framework may look simple enough, adding a few of them together makes the mental noise the coder has to suffer overwhelming.
I am not a huge fan of Rails, but they have usability nailed, at least for 80% cases. In this respect, Java has much to learn from it.
I think the ideal framework has a full API available for 20% cases, supplemented by a "builder" or "configurator" for 80% cases that presents it in a more usable way, eg as an embedded DSL.
If a language has a cool feature, then this feature can be used to make cool frameworks.
For example, if Java had lambda functions, its UI frameworks would be much easier to program for.
Personally, for business applications, I would pick Java, db4o for persistence, and I would make a simple UI framework of my own that would allow me to declare web pages using standard Java.
Db4o is all one needs for persistence: it automatically saves object graphs, and queries can be expressed with Java expressions. It also supports transactions. It's the easiest thing on Earth. It puts all other database systems to shame.
For the UI, all I would do is a thin layer of classes that would be used to render an HTML document. For example, if I wanted to make a login screen, it would be as easy as this:
Page loginPage = new Page("Login");
Table table = new Table();
loginPage.add(table);
Label label1 = new Label("username");
table.add(label1, 0, 0);
TextBox username = new TextBox();
table.add(username, 0, 1);
Label label2 = new Label("password");
table.add(label2, 1, 0);
PasswordBox password = new PasswordBox();
table.add(password, 1, 0);
Button loginButton = new Button("Login");
loginButton.addListener(new ButtonListener() {
void click() {
login(username.getText());
}
});
page.add(loginButton, 2, 0);
Of course, if Java supported tuples, named arguments and closures, the code would be:
Java frameworks are over-engineered: too bloated, too many features, too complex.
The best feature that a programming language can have is the ability to compose new functionality out of existing one. Functional programming languages are built around this concept, Java isn't. Ruby is, because it's dynamic nature allows the composition of anything with anything.
Here is an example of "How Does Language Impact Framework Design". Frameworks need callbacks. Callbacks are the small, little pieces of plugin code that a developer writes, which allow them to interact with the framework.
In other languages, these tend to be easy to write (closures), but Java makes them hard (inner classes, or reflection Method objects).
This is why I believe that adding better support for callbacks will dramatically improve the Java landscape - because new and improved frameworks will be able to rely on users being able to easily pass in their business logic as callbacks.
> <p>On that note though, the more I learn about Ruby, I'm > beginning to think that just as Java doesn't easily scale > down to smaller tasks, Ruby wouldn't scale <em>up</em> to > large systems: The way Ruby classes can be opened up and > parts of a class redefined—a very handy feature when > writing Rails modules, for instance—can, for instance, > lead to completely incomprehensible programs in the wrong > hands.</p>
That's true, but in the wrong hands nothing is safe and that trumps anything you can do in a language.
Ultimately, code comprehension and maintainability are social rather than technical problems. I've seen exquisitely good large systems written in C, and lousy systems written in much more capable languages. It all goes back to the people.
This is a question that currently interests me greatly. I have built a tool called web4j (see web4j.com, if interested) - yes, yet another web app framework. And I have hit a bit of a wall with it.
The code of applications built with web4j is indeed compact, but there are still some annoying repetitive aspects that I just can't remove in a reasonable way, using standard object programming techniques. In addition, Java is a bit verbose in places.
There seem to be three ways of dealing with code repetition: 1) use standard techniques of object design 2) code generation 3) define a domain language
The point is that in Java, using just #1 produces decent, but not wholly satisfying, results.
For my web4j tool, I am now building a code generator to act as a simple typing aid (a 'passive' code generator, in the jargon of the Pragmatic Programmers.) My hope is that I will be able to pursue #3 - defining a domain language. Given the complexities of adding such a capability directly to Java itself, I think I would prefer to use another language *on top* of Java (not instead of it).
Our architecture team has built a Java-based web framework that has given us tremendous performance and scalability improvement. We could do better on usability, but our teams seem to have adopted it just fine. This even with doubling out staff over the last year.
I would not say our framework is cool but it is definitely reliable as it powers one of the top e-commerce sites.
From a language perspective, we highly value Java type safety. Our philosophy is to catch as many errors during compile time as possible and with Java this is feasible. To this point, we have decided that languages like Ruby are not right for us when programming in the large; dynamic typing seem to us a risk we don't need to take. For small, low risk projects, the decision might be different.
We do have some pain points with Java. The obvious one is the language's verbosity. An equivalent language could be much more terse and provide the same benefits. In practice though, with IDEs and code completion, this isn't a big deal. I suppose for people who can't touch type this is more of a pain point. I see fewer and fewer developers who can't touch type.
The two major feature-related pain points are with generics and the lack of closures. We make heavy use of generics and we find that Sun's partial implementation of generics leads to all sorts of problems and confusion. I've read several blogs lately claiming that generics are just too complex regardless of whether erasure gets erased. I disagree. Our staff struggles with the incompleteness much more than the notion in general. We aren't a push-button programming organization, so we hire competent developers. I'll revisit this last point at the end of my post.
The other feature we constantly wish we had is closures. We encounter many situations where we desire to run a piece of code in our framework context. This can be done with inner classes or 'method classes' but it would be so much more elegant and powerful to pass a closure. We have lived without closures so far, but the solutions Java now provides are convoluted and lead to some confusion and code bloat.
Now I want to get back to the competent developer point. Good frameworks have many potential benefits. One is hiding complexity; another is narrowing the development path. We use our framework more for the later than the former. We have some hard problems to solve that sometimes defy complexity hiding (at least in total). So in those cases, we emphasize narrowing the options the programmer can take by providing patterns and idioms within our framework.
My experience is that frameworks are like so many other tools: Except in the simple cases, they don't enable weak programmers to solve hard problems. Rather frameworks enable safety for the weak programmers, and also powerful efficiency for the strong programmers. The strong programmers are going to benefit far more from good tools. I feel the same way about IDEs, RAD environments, etc.
> From a language perspective, we highly value Java type > safety. Our philosophy is to catch as many errors during > compile time as possible and with Java this is feasible. > To this point, we have decided that languages like Ruby > are not right for us when programming in the large; > dynamic typing seem to us a risk we don't need to take. > For small, low risk projects, the decision might be > different.
The real question is what is the cost and benefit of including type declarations. The cost includes the below point:
> We do have some pain points with Java. The obvious one is > the language's verbosity. An equivalent language could be > much more terse and provide the same benefits. In practice > though, with IDEs and code completion, this isn't a big > deal. I suppose for people who can't touch type this is > more of a pain point. I see fewer and fewer developers who > can't touch type.
The question isn't how much it is to type - it is how much it is in the way of reading.
When you have readable code - and readable code include *compact* code, so you can get the maximum amount of context into a single screen - you have less errors.
When you have a bunch of extra unnecessary stuff in the code this is a cost to reading what the code does.
Type declarations sometimes are beneficial for reading code (e.g, at the edge of a module you otherwise treat as a black box, or for a variable that's initialized some distance off), and the rest of the time they are noise that distract from reading. They only distract a little bit, but that's still a cost. The programmer needs to use a tiny bit of cognitive power to ignore them. (Don't come talking to me about "That's about lack of experience" - I have 20 years of experience with programming with the type declarations.)
Typing is incidental. It's a very minor thing.
The noise of type declarations also is one of the reasons I think the frameworks of Java end up the way they do. Your program will be noisy no matter what you do, because you've got all this boilerplate you have to repeat. Then, adding some more boilerplate - e.g, a configuration for how to map tables to class names - does not seem like a big deal. You gain flexibility, after all, and it's consistent to have a single place where you declare all such mappings instead of having to declare exceptions to the general rule. But - that lead to a ton of declarations, and most of them are the obvious ones. This hides all the non-obvious variation in this.
Language also influence frameworks in a different way: A feeling of a language being "solid" and "complete for the task" makes the frameworks want to be the same - to have them cover the entire space, and cover it solidly, meaning that you get a ton of flexibility and a lot of complexity. An example of this is Hibernate. As far as I can tell, Hibernate lets you map the database to most reasonable object models, and it hide the loading of data and storing of data to the database for you. You get "perfect" hiding of the database - except when it doesn't work, and you actually need to hit the database directly, or you need to understand your performance characteristics.
Contrast this with the ActiveRecord pattern, of which the most well-known variant is Rails ActiveRecord class. The ActiveRecord pattern is simple: One database row, one object. This creates a very "thin" mapping, and makes it obvious to the person working with the program how this maps to the database - and where performance hits come.
You get a large difference in the culture around frameworks, where the languages where you can write things so they are simple and small also get compact libraries with small APIs and simple abstractions - because when you add complexity, it is so obvious that you do so.
I've worked both in java Web world (struts, weblogic, etc) and ASP.NET.
I don't think VB.NET or C# (version 1) are much better languages than Java, but the ASP.NET framework is pretty friendly and fun to work with compared with what I've had to work with in Java. And others in the ASP.NET world are excellent too, witness the <a href="http://subsonicproject.com">SubSonic project</a>.
I don't see what VB.NET or C# version 1 have that Java do not have that prevent impedes Java having great web frameworks. In fact I hear Wicket is an excellent java web framework.
I have believed, and have it confirmed frequently, that much of our discussion in the java community about Java and it's shortcommings is more subjective than we we're willing to admit. There is an industry or occupation based on describing, tutoring, and evanglizing the next best thing and we often manufacture it for ourselves.
I was struck a few years back when Richard Monson-Haefel, author of several EJB books, pretty much came to the conclusion that it was an over architected framework and moved on http://www.infoq.com/news/Java-EE-Demise-Report. He was responsible for parts of it, it boggles the mind.
(http://www.monson-haefel.com/, I guess he's into power, he has the Japanese/Chinese symbol for power next to each blog posting, don't know what that's all about, I think the KISS drummer used to have that on his bass drum).
What happened was that the newness wore off and it was time to move on. Some of the folks more sensitive to winds of change realized Ruby was going to be popular and washed Java out of their hair. Others kind of had the Java scent, and need something else to move on to, and it looks like Scala is in the process of being chistenned here at artima.com. Consider for example that we could have done RESTful services all along with Java, and pretty URLS...there's nothing intrinsic about Java that prevents us from doing so, however, Rails lead the way...notice you'll never see a .this or .that. Same thing with XML, Rhino and bean shell would have sufficed for app configuration.
Note that I changed the login(username) call to login(username,password). Also note that Username and Password aren't strings here: they're handles. Handles have methods like get() and set() to get and set the value of the text entry fields.
The GUI is ready to be displayed:
{{QTk.build LoginPage} show}
The first thing you noticed is Oz' weird syntax. Curly braces are used for function calls and parens are used for tuples: the opposite of your example syntax for Java.
Oz has the same language features as your extended Java. Tuples: the GUI layout is specified using tuples and records (records are tuples with named fields). Oz has closures, so we can specify the action: property of the login button inline, and we automatically have access to the Username and Password variables.
One feature of Oz that is used which your extended Java probably doesn't support is dataflow variables. They allow you put uninitialized variables, like Username and Password, in tuples and records, and they can be initialized later. In this case QTk initializes them for us.
All these language features are necessary to build this concise and declarative GUI framework, so in this case the answer to the question of this post is: language features are critical for framework design.
As someone who normally works with ColdFusion (dynamic curly brace language) and has been learning Hibernate, I can say that if you haven't worked in both worlds is hard to appreciate just how much more streamlined Java IDE's are. This is not just tool market maturity, it's a direct consequence of static typing. My learning style involves lots of code reading, and navigating around a large code base in a dynamic language is truly painful. The relatively porous APIs of Java frameworks may not be to everyone's taste, but it's the language itself that enables the tooling to make them manageable.
> Now I want to get back to the competent developer point. > Good frameworks have many potential benefits. One is > hiding complexity; another is narrowing the development > path. We use our framework more for the later than the > former.
Hear, hear. This is also a benefit of inheritance (via template method patterns) that is often overlooked in the inheritance vs composition debate.
> The real question is what is the cost and benefit of > including type declarations. The cost includes the below > point: > > > We do have some pain points with Java. The obvious one > is > > the language's verbosity. An equivalent language could > be > > much more terse and provide the same benefits. In > practice > > though, with IDEs and code completion, this isn't a big > > deal. I suppose for people who can't touch type this is > > more of a pain point. I see fewer and fewer developers > who > > can't touch type. > > The question isn't how much it is to type - it is how much > it is in the way of reading.
I believe the reading argument is very weak. I won't speak for the rest of my staff, but it has *never* come up.
Anyway, the reading cost is nothing compared to the cost of failing to detect errors until deployment (or even QA). Of course Unit testing helps but things do get through.
> When you have readable code - and readable code include > *compact* code, so you can get the maximum amount of > context into a single screen - you have less errors.
I agree to a point. My issue with Java verbosity is that the language requires one to repeat his/herself.
SomeFooClass foo = new SomeFooClass(a, b);
I am more bothered by the horizontal verbosity. And this does come up occasionally.
> Typing is incidental. It's a very minor thing.
That's just nonsense. On small projects with one developer, where code doesn't get changed much over time, or where the developer doesn't stick around to observe the long term effects, this may hold true.
> e you've got all this boilerplate you have to repeat.
On this we agree but, as I said, in practice it's not one of the significant costs.
> Then, adding some more boilerplate - e.g, a configuration > n for how to map tables to class names - does not seem > like a big deal. You gain flexibility, after all, and > it's consistent to have a single place where you declare > all such mappings instead of having to declare exceptions > to the general rule. But - that lead to a ton of > declarations, and most of them are the obvious ones. This > hides all the non-obvious variation in this.
What point you are trying to make?
> Contrast this with the ActiveRecord pattern, of which the > most well-known variant is Rails ActiveRecord class. The > ActiveRecord pattern is simple: One database row, one > object. This creates a very "thin" mapping, and makes it > obvious to the person working with the program how this > maps to the database - and where performance hits come.
I figured this was coming. Rails is fine for a web site that babysits a database. But if you need to do something complex, you are SOL. Yes, some problems actually are complex and you need a language that helps. Our problems are complex and we get benefit from the 'constraints' of Java and even the complexity of Hibernate. I wouldn't choose these tools, nor would I recommend building a framework, if my application were a DB babysitter app. I'd use the best tool; probably RoR.
> You get a large difference in the culture around > frameworks, where the languages where you can write things > so they are simple and small also get compact libraries > with small APIs and simple abstractions - because when you > add complexity, it is so obvious that you do so. > > Eivind.
Hopefully you didn't interpret that I was advocating writing frameworks to add complexity.
> I have believed, and have it confirmed frequently, that > much of our discussion in the java community about Java > and it's shortcommings is more subjective than we we're > willing to admit. There is an industry or occupation > based on describing, tutoring, and evanglizing the next > best thing and we often manufacture it for ourselves.
Ain't that the truth! I get so tired of the Koolaid sucking masses of programmers who stare glazzy-eyed and recite in monotone the benefits of EE, Ruby, SOAP, BEPL, ESB,... They don't even try to understand the real value propositions of new technologies, or whether they apply to their own problems.
> What happened was that the newness wore off and it was > time to move on.
Many in our industry are so self-absorbed that all they want to do is work on the next popular thing and get it on their resume. They don't give a damn about value unless its part of their personal bottom line.
> Consider for example that we could > have done RESTful services all along with Java, and pretty > URLS...there's nothing intrinsic about Java that prevents > us from doing so, however, Rails lead the way...
I'll take Restlet over Rails any day. I'll decide what convention is right for my company, thank you.
Flat View: This topic has 42 replies
on 3 pages
[
123
|
»
]