Friday, November 6, 2009

The Closure Compiler turns a Pattern into an Antipattern

I have been waiting months to publish this blog post. When I attended The Ajax Experience 2009, I saw a number of "misguided" things that people were doing with JavaScript.* Knowing that Google was in the process of open-sourcing the Closure Compiler and the Closure Library (I was working on the Compiler effort before I left), I wanted to get up and yell, "Stop -- you're all doing it wrong!"

But I didn't.

The Closure Library and Closure Compiler that Google released yesterday are game-changing. I have always heavily subscribed to Fred Brooks's No Silver Bullet argument, but it's possible that the Compiler will improve your web development processes by an order of magnitude. Errors that previously may not have been caught until manual testing can now be caught at compile time. Before the Closure Compiler came along, there wasn't even such a thing as "compile time" when it came to JavaScript!

The Compiler is complex -- it will likely take some time for developers to wrap their heads around it. It was hard to write JavaScript at Google without also thinking about what the Compiler would do to it. For sure, the Compiler will change the way you write JavaScript code.

To illustrate one of the things that will change, I put together a detailed article on inheritance patterns in JavaScript that compares the functional and pseudoclassical object creation patterns. Now that the Closure Compiler is available, I expect the pseudoclassical pattern (that the Closure Library uses) to dominate.

Special thanks to Mihai and Kushal for reading drafts of the inheritance article.

*I saw a number of innovative things at the conference as well, but none that would have the same impact as the Closure Compiler. However, there was one presentation at The Ajax Experience titled The Challenges and Rewards of Writing a 100K line JavaScript Application that revealed that a company called Xopus in the Netherlands had built their own JavaScript compiler. It is the closest thing I have seen outside of Google to the Closure Compiler. My only objection to it is that I believe that the input to their compiler is not executable on its own.

For example, slide 12 shows some sample code that calls Extends("com.xopus.code.Animal") which has no apparent reference to the class doing the extending, so it is hard to imagine how calling Extends() has the side-effect of providing Animal's methods to Monkey. Presumably, the compiler treats Extends() as a preprocessor directive whereas Closure's goog.inherits() will actually add the superclass's methods to the subclass's prototype. Closure generally relies on annotations, such as @extends, for compiler directives rather than function calls. Closure code should always be executable in both raw and compiled modes.

8 comments:

  1. You can find more details on using Google Closure API with ASP.NET at Using Closure Compiler with ASP.NET - A C# Wrapper .

    It features a C# wrapper for the tool.

    ReplyDelete
  2. I presented about the Xopus App Framework at the Ajax Experience. I'd like to emphasize that our raw source code does in fact execute without preprocessor. All you need is a script with the compiler (written in JavaScript). We use that property for unit testing and dynamic loading of code.

    It is true that the code doesn't look very executable, but that is because we sacrificed a clean compiler implementation against readable source code.

    Our compiler relies on the order of the directives. Like you say about the Google Closure Compiler, at Xopus we also have to follow the rules of the compiler (but you can insert plain JavaScript if you need for rapid prototyping). Each file must start with a Package directive followed by one of more Import, Extends or Resource directives followed by a Class directive. The compiler figures out how to build a prototype chain from that.

    ReplyDelete
  3. Excuse me, but what exactly is the pattern and what is the anti-pattern?

    ReplyDelete
  4. As explained in the article (http://bolinfest.com/javascript/inheritance.php), I argue that the functional pattern should now be considered an antipattern now that the Closure Compiler removes the hazards of the pseudoclassical pattern. As measured by Erik (http://erik.eae.net/archives/2009/11/09/21.12.16/), the functional pattern can be quite expensive in terms of both memory and speed.

    ReplyDelete
  5. Thanks for the answer. Now it's all clear.

    Sidenode: What's wrong with wrapping a pseudoclassically defined class in a (function(){})(); to provide private state to the prototype functions?

    ReplyDelete
  6. filibeli: You should not use the REST API for serious compilation (in this case, that means files over 200000 bytes). See http://groups.google.com/group/closure-compiler-discuss/browse_thread/thread/cc51721d85ee5209# As discussed on the thread, you should use the compiler locally for larger projects. That will also eliminate the network latency introduced by the REST API and allow you to compile offline!

    ReplyDelete
  7. fforw: I'm not sure what you mean or what the motivation for doing that would be.

    ReplyDelete
  8. Laurens: does that mean Package(), Import(), and Extends() are stateful? That is, they act on the last thing passed to Package()? That must result in some nasty bugs if you forget the Package() line!

    ReplyDelete