Coffeescript Static Analysis / Static Typechecking - Roadblocks Coffeescript Static Analysis / Static Typechecking - Roadblocks javascript javascript

Coffeescript Static Analysis / Static Typechecking - Roadblocks


This answer is a bit of a brain dump since I'm interested in this also. Hope it helps.

I use the Google Closure Compiler to statically analyze the code that CoffeeScript generates. It has a really good static analyzer, and I'm not sure if there's a good reason to reinvent the wheel here. The easy way is to just write the annotations by hand:

###*   * @param {number} x   * @param {number} y   * @return {number}###adder = (x, y) -> x + y

It's a bit verbose, but on the other hand you're borrowing the static analysis abilities of the closure compiler which is really powerful and is able to check a lot. I actually write type annotations in a slightly more concise way, then have a script to rewrite the coffee file. My code ends up looking like this:

#! {number} x {number} y @return {number}adder = (x, y) -> x + y

I'm sure you can see that the rewriter is pretty straightforward.

A quick note before I move on. Be sure to compile your code with -b (bare) if you're running it through the closure compiler. The closure compiler is pretty good, but it's not smart enough to do data flow analysis. CoffeeScript wraps your code in an anonymous function by default, which will trip up the compiler.

Another option along the same path (this would break compatibility with CoffeeScript, but would be a lot cooler) would be to have the Coffee compiler compile something like this:

adder = (number x, number y): number -> x + y

into JS like this:

/***  * @param {number} x  * @param {number} y  * @return {number  */var adder = function(x, y) {  return x + y;};

which could then be fed into the closure compiler on a compile - if there were no errors the compiler could then strip all the comments out.

Indeed, this guy appeared to be doing exactly this. Sadly, his work seems to be in an incomplete state.

In all of these cases, we defer the hard work - static typechecking - to the closure compiler. If you don't want to do this, I'd understand, but it'd be tough to convince me that it's worthwhile to build a whole new static analysis tool from scratch. :)

EDIT a year later: I just use typescript these days. :)


I'm not an expert in CoffeeScript, so this might be the completely wrong answer, but it basically boils down to this: CoffeeScript is a very expressive language, with most of the semantics dynamically determined (and possibly strange edge cases). This is in much contrast to languages like Standard ML, which have a much more strictly defined semantics. In general, doing static analysis on higher order languages is considered very hard. I.e., static analysis on real higher order programs (Haskell, ML, especially javascript because of things like eval) is just hard because the flow of control is much more flexible. In fact, the static analysis solutions for higher order languages have really only been explored within the past twenty years or so. (Notably, see Matt Might's article on a tutorial style description of CFA.)

Basically, the reasons are this:

  • To do analysis, you have to deal with the problem of the expressive semantics coming form the flow control you get by slamming around higher order functions.
  • To do typing, typically these languages have a much richer set of types that are available. For example, there are very typically situations where if you try to assign a static type to a variable in Ruby (as in C, Java, ML, etc...) you get an error, but because certain paths of your program are never executed, it's all fine. Along with that, languages like Ruby others add a plethora of implicit type conversions that are really used to do cool programming. The notable work in this area with which I'm familar (dynamic analysis of static types for Ruby) comes from some of the people I work with, but there are certainly other examples.
  • Basically, the language is used in a much more dynamic way, with a much more expressive semantics, and reasoning about that statically is much harder, and prone to be imprecise. The basic front of approaching this (these days) is starting to look hybrid: you can statically analyze part of a program, and than also require the programmer give some test cases to do some kind of refined analysis.

I hope this somewhat answers your questions, again, sorry I can't address directly the direct concerns of your question as it applies to CoffeeScript, but there's a lot of work going on in analyzing things like JavaScript right now. I'll note that some of the real problems with Javascript come from it's strange semantics, the prototypical inheritance is hard to reason about, and especially eval()! Typically program analyses for these languages impose certain restrictions (for example, throwing out eval completely!) to make the analysis more feasible!