Error formatting macro: pagetree: java.lang.NullPointerException
Skip to end of metadata
Go to start of metadata
You are viewing an old version of this page. View the current version. Compare with Current  |   View Page History

Problem Statement

In Clojure every expression is a value, however for targets like JavaScript this is not true. JavaScript makes a distinction between expressions and statements. JavaScript dictates where and when expression may appear - the most flexible type of expression is the function expression - it is allowed to appear nearly anywhere. This allows us to write compound tests (with `and` and `or`) for if expression - in the compiled JavaScript the test expression will be wrapped in a function that is invoked immediately. While this solves the problem this incurs measurable overhead particularly in critical sections. It is undesirable to provide access to lower level JavaScript boolean operators - it's preferable to allow users to write straightforward code and to optimize this as part of the compilation process.

Originally this was done via the Google Closure Compiler. This worked reasonable well, though Closure could be a bit finicky, immediate function invoke nested to a certain level will not be optimized away, in fact if it's deep enough they'll be left entirely alone. This requires tedious review of the generated JavaScript and manual lifting in the ClojureScript code. While tedious this more or less worked and it was hand optimization that rarely need be applied outside of the most critical sections - Closure would catch most of the cases with simple optimizations (note immediate function invokes not removed by simple optimizations would not be removed with advanced).

Unfortunately the most recent Closure Compiler dependency no longer applies the above optimization slowing down nearly all critical functions that have simple boolean tests involving `and` and `or`. This is because `and` and `or` macroexpand to `let` + `if`.

Possible Solutions

  • Write a simple AST rewriter for the specific case
    • Benefits - easy to implement
    • Tradeoffs 
      • with few improvements to the 2 years old AST design, the cruft is growing and this will only add to it
      • no other forms that generate immediate called JS functions will be optimized
  • Design a pluggable AST pass system
    • Benefits
      • force us to make some basic improvements/standardization around the AST
      • allow any future optimizations to be handled in a modular fashion
      • no longer depend on Closure Compiler for language optimizations that may be ignored because less applicable to mainstream JS practice
    • Tradeoffs
      • More design, more work
        • Needs to consider the AST
        • Need a future looking optimization design
        • Lots of room for potential breakage and unforeseen edges cases