I think Coffeescript is an awesome language! I was looking for some projects / issues / features that add Static Analysis to Coffeescript. However after some searching I found that the Coffeescript faq and this page suggest that static analysis might not be viable.
I was wondering that if there is a fundamental issue in implementing static analysis / static type checking in Coffeescript, because of which something of this sort does not already exist in the compiler?
Also, is it something that is not possible to do for non trivial checks but might work only for straightforward analysis? When I say straightforward I mean checking for trivial stuff like, whether the user has defined a function twice by the same name (in a class) or on the top level (or perhaps on the top level in a collection of related .coffee files).
I would appreciate if anyone could please point out some examples that show why implementing static analysis / type checking is not straightforward / possible / worth spending time on?
Thank you very much!
CoffeeScript is a programming language that compiles to JavaScript. It adds syntactic sugar inspired by Ruby, Python, and Haskell in an effort to enhance JavaScript's brevity and readability.
CoffeeScript. CoffeeScript is a little language that compiles into JavaScript.
Basically, CoffeeScript was made by people who hate JavaScript syntax, intended to be used by other people who hate JavaScript syntax. It's compatible with all flavors of JavaScript, and adds in “syntactic sugar” from other programming languages in the same way you sweeten up your morning cup of Joe – hence the name.
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:
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With