Monday, June 04, 2007

Required Reading: Enforcing Strict Model-View Separation in Template Engines

So I've been reading Terence Parr's excellent addition to the Pragmatic Programmers catalog: The Definitive ANTLR Reference. This book is a lot of fun for me (I recommend it highly, btw), because it's gotten me thinking again about languages and language implementation issues in a way that I haven't had to since my first compilers class. Dr. Parr has a relaxed and informative writing style that can make you forget you're reading about some pretty hardcore computer science.

I wanted to do some more reading on StringTemplate, the templating engine ANTLR uses to emit the results of its translation, because I read that it can be (is) used to build websites as well. My searching led me to Dr. Parr's paper from 2004: Enforcing Strict Model-View Separation in Template Engines. In it, Parr explains -- and most importantly formalizes -- his ideas about the need for strict separation in view templating mechanisms, and how the vast majority of existing solutions (e.g., JSP, Velocity) fall far short, ironically because of the Turing-complete power they provide.

We Java programmers who have been around since the introduction of JSP have already been informally exposed to some of these concepts, in the form of the Model 1 -> Model 2 architecture evolution. One of Dr. Parr's key points, though, is that it's not enough simply to recommend avoiding using the full computational power available in (for example) JSP scriptlets. The temptation to use that power, even in seemingly innocuous ways, is simply too strong to be resisted for long. Parr's insight that having full access to the Turing-complete power inherent in JSP/Velocity/what-have-you actually gets in our way as developers reminds me of the 37Signals take on embracing constraint.

Mind you, I don't expect to be organizing a revolution around replacing my company's use of JSP with StringTemplate anytime soon. But I do plan to develop the personal discipline of following Dr. Parr's advice and embracing constraint by voluntarily avoiding the constructs that violate strict separation. That statement may seem to contradict the statement above about temptation being too strong, but I think that if enough individuals had the same resolve, we could improve the situation somewhat until a move to StringTemplate or its equivalent became politically feasible.

Closures in Java: Continued

In the comments section of my last post, user sanity makes the following astute observation, re: my closing remark about Java being a Turing complete language:

Well, pretty-much every programming language is Turing complete, so by that argument, why bother adding any feature to any language?

To which I reply: "Bravo!" and "Well said." I'll even take it one step further: anything that can be done in a high-level programming language can be done in assembler language, or even machine code. I know from hard experience, having implemented an encapsulation/data-hiding scheme using IBM mainframe assembler language, and having patched code using the raw machine opcodes in the same environment. Ah, the good old days!

So I'll go back to programming in assembler language, and the rest of you can either join me or take a well-earned break. Thanks for playing.

All right -- as much fun as the reductio ad absurdum game is, all it does is reduce the argument to an absurdity. It doesn't really point to a constructive resolution. Although, in this case, it does raise the question (note: notice I did not say, "begs the question," which is a solecism that I will reserve for another post) of the degree to which the effort involved in constructing a higher-level language offsets the effort involved in writing code in a low-level language.

I think we can all agree that programming in C or C++ is more pleasant and more efficient in terms of our time than programming in assembler language. You give up some flexibility (e.g., the ability to stuff a specific value in a specific register, direct access to all non-protected memory), but you gain recursion, stack management, register coloring -- all that good stuff.

Move up to Java, or Objective-C (version 2.0; coming soon!) and you get the added relief of automatic memory management (allocation and garbage collection). With Java and its ilk, you also get checked exceptions, which you may or may not appreciate.

Move up to Ruby (JRuby), or Python (Jython), or LISP, and you get dynamic typing and language/runtime-level support for first-class functions/blocks/closures.

So switching between these flavors of Turing-completeness makes sense. If you need close-to-the-metal performance with some measure of portability, use C. If you need rapid prototyping, use Ruby or Python. If you need to justify spending $300+ on IntelliJ IDEA, use Java. (Sorry, couldn't resist).

My point (and I do have one) is that the difference between Turing-complete-Java and Turing-complete-Java-now-with-closures! is just not worth the effort it looks like adding this nice-but-not-essential feature to the language will require.

Closures in Java will muddy an already-dense syntax even further. They will require more code butchery and internal gymnastics on behalf of the compiler. They will be devilishly hard to add in a way that makes sense, given Java's baked-in typing restrictions and flow control. And, in the end, they're going to satisfy the demands of a vocal minority. The masses will still take the path of least resistance, and will probably avoid closures like the plague. And those of us who make a point of being aware of non-Java programming idioms will appreciate the differences between the many available languages, and will continue to strive to use the best tool for the job at hand.