CoffeeScript -- You Are Totally Gonna Hate It
slideshare.net
slideshare.net
It was surprisingly enjoyable. I can see this ecosystem giving Rails/Sinatra/Django a real challenge. CoffeeScript makes ssjs a lot easier once you get used to it, Node.js gives you free non-blocking/high concurrency, and Express, which just released 1.0, is quickly maturing into a really solid DSL/web framework.
I'm excited to see how this ssjs scene unfolds.
http://shootout.alioth.debian.org/u32/code-used-time-used-sh...
Thanks to the effort put in by the V8 team, JavaScript is now an order of magnitude closer to C speed than Ruby, Python, and Perl, and sits at the same tranche of performance as OCaml and Go.
http://github.com/aaronblohowiak/ejsrb/
I think that the usual way to share views between different languages is to use a language-agnostic templating engine, like Mustache, or Liquid.
How does EJSRB differ?
Furthermore, they are not really trying to promote the language beyond its current niches.
That said, there are good third party libraries and frameworks, and a Rubygems-like system called Lua Rocks.
The fact that Javascript is an in important feature in software that everyone runs is a whole different kind of advantage.
I've found the combination absolutely brilliant -- it recalls Ruby's creator Matz's promise of a language to "make programmers happy".
You get the expressiveness and brevity of Ruby, plus first-class functions, running in anyone's browser; and you get really well-thought-out event handling, AJAX and animation APIs.
I have only one serious pain-point with CoffeeScript: the Pythonesque significant whitespace. This commonly trips me up when providing anonymous function arguments to other functions (very common when assigning event listeners, for instance).
I guess the problem is that I never really understand exactly which whitespace is going to be deemed significant -- so maybe it's just a matter of documentation?
There was a question in #coffeescript last night about passing multiple anonymous functions into a function, and we came up with a couple of nice ways to write it:
I should have mentioned that's the other thing to love about CoffeeScript: insanely responsive support!
I like the lambda syntax (I don't recall seeing exactly this syntax before):
() -> alert("lambda")
(() -> alert("lambda")) () # to call it -> alert 'lambda'
(-> alert 'lambda')() # to call itI mean:
map x -> xx seq
might be readable ..
but:
map x -> xx seq seqs
is less clear.. while:
(map (x -> x*x) (seq seqs)) is more readable.
I'm thinking out loud, mainly because I tend to use the simplest syntax possible when coding in languages that allow for multiple syntaxes (i.e., Ruby). Most of the time, my decisions (at least) work out for me, and I don't have to go back and (say) add parentheses.
print("Hello");
And this: print "Hello"
(Which are equivalent in CoffeeScript) ... I'll take the latter every time. () => expression
arg => expression
arg, arg2 => expression
arg => { statement; expression }http://jashkenas.github.com/coffee-script/#heredocs
And extended the section on "cake" a little bit with a better example that demonstrates passing in a command-line option. It sounds like your problem is different though -- here's the Node.js docs for child_process.exec:
http://nodejs.org/api.html#child_process-exec-95
You can use the "stderr" argument in the callback function to print the child's error output to the console. Running this bit of CoffeeScript, for example, will print out "to stderr":
{exec}: require 'child_process'
exec 'echo "to stderr" >&2', (err, stdout, stderr) ->
print stderr fs: require 'fs'
fs.readFile source, (err, code) ->
try
js: CoffeeScript.compile(code.toString(), {source})
fs.writeFile 'out.js', js
catch err
console.log err
Here's the source of the "CoffeeScript.compile" method:http://jashkenas.github.com/coffee-script/documentation/docs...
one part i'm ambivalent about though is assignment and conditionals. there are too many ways to do the same thing, and i sit there wondering whether i'm using the "right" one.
JavaScript is good, but CoffeeScript removes many of its imperfections, as well as offering a far better syntax.
As for watching a directory for changes, absolutely. Just pass "--watch" to the "coffee" executable. For example:
coffee --compile --watch --output lib src/
coffee -cw -o lib src/
Which will watch "src" for changes, and compile all ".coffee" files inside to ".js" files in "lib", preserving the directory structure. if x = y
The compiler should raise a syntax error.So, although '=' is available to make transitioning from other languages smoother, idiomatic CoffeeScript would use ':' for assignment, both within and outside of object literals, and would use 'is' for equality. Harder to make a bug out of:
if x is yIncidentally, what's the most successful recent language that tries to make functional programming terser by not having an explicit "return" keyword (everything's an expression)? I'm genuinely curious, and don't think any significantly great number of programmers will ever find this style easier.
(Technically Ruby does have a return keyword, but it's optional. Same goes for CoffeeScript, I believe.)
But you are quite right that Perl if/else are statements and not expressions. Instead you can use the ternary operator:
my $value = $cond == 1 ? 'true' : 'false';
And because of ditto for Perl you can also do following: my $value = do { if ($cond == 1) { 'true' } else { 'false' } };I don't, however, think that it's about functional programming. It's about flexibility: if everything is an expression, you can use any bit of code in relation to any other, and not worry about what parts of the languages are merely statements ... a JavaScript example being "var a = b", which cannot be used as part of a larger computation.
Having every function return a value is something that seems extraneous at first look, but ends up encouraging good API design. From the point of the caller of the function, returning a meaningful value is always appreciated -- even if it's as simple as returning "true" to acknowledge that the operation was completed successfully.
int[] a = int[]{2,3,3,4}
int s = a.Sum(i => i)
Thats C# by the way - lambda statements have implicit return statements.Let me see, Ruby, Haskell and Scala uses this syntac.
Did the language remove any of the "bad parts"?
http://www.ecma-international.org/publications/files/ECMA-ST...
Most of the strict mode restrictions have to do with "arguments" and "eval", which CoffeeScript doesn't affect. The two bits that we do enforce are: "Assignment to an undeclared identifier or otherwise unresolvable reference does not create a property in the global object," and "Strict mode code may not include a WithStatement" -- the former because variable scoping is automatic, and the latter because there is no "with" statement in CoffeeScript.
But there are a number of other, more important bad parts that are omitted, beyond strict mode: JavaScript's string-based switch statement, coercive equality checks, named function statements, trailing commas, and so on...
For example, here's a hypothetical message:
email: """
Dear $recipient,
You should receive your $product
in the mail in $product.eta days.
Thanks,
$sender
"""I'm building my own javascript preprocessor language called O. I will definitely have to use that one, I hadn't thought of adding that. Should have considering it's in PHP, Pything, etc.
ps. I like the look of coffeescript, and the principle of having everything be an expression. What other kinds of properties does it enforce? Are you going to be adding any more features/property-preservations?
I'm trying to shoot for two interesting properties, 1) To be able to compile it back down to C/C++ for server side speed increases(Say in projects like Node.js where you can pretty easily hook in C/C++ modules. 2) To have the property of all code be reversable, and branchable for some interesting use cases. The reversability ties into some Networking/User-Interface Libs I'm making.
I'm curious what kind of tricks you used for implementing Coffeescript. Did you use hand made parsers, Parsing Expression Grammars, or base it on any other good projects?
If you'd like to discuss the implementation, feel free to drop by #coffeescript on freenode. But to give you the rough outline -- the compiler is made up of (in order), a Lexer, Rewriter, Parser, and AST of Nodes. The lexer creates the tokens, the rewriter rewrites the stream of tokens, disambiguating parses and allowing for optional syntax, the parser generates the AST of nodes, and then "compile()" is called on the root node, and walks down the tree, compiling the JavaScript string recursively.
For the parser, I'm using the excellent Jison parser generator for JavaScript, in LALR(1) mode. The only really unorthodox part of this is the Rewriter. It's not kosher to munge a token stream before parsing it -- but it's removed a ton of complexity from the grammar to not ever have to handle the syntactical edge cases, and to have them resolved in advance. All of the source is annotated, so here's some links:
* Jison: http://github.com/zaach/jison
* Lexer: http://jashkenas.github.com/coffee-script/documentation/docs...
* Rewriter: http://jashkenas.github.com/coffee-script/documentation/docs...
* Grammar: http://jashkenas.github.com/coffee-script/documentation/docs...
Thanks for the pointers, I remember now finding those implementation details when I leafed through your code a while back.
I'll check out the freenode channel when I get a chance.
http://github.com/jashkenas/coffee-script/blob/master/lib/le...
Pretty readable. You can use a Webkit or Firebug debugger just as you would with normal JS. It's a golden rule (with one small exception) that there are no CoffeeScript-specific constructs or special functions added to the runtime, no matter how tempting it might be to do so.
The single largest headache in practice is having to refer to the generated JS if the exception is vague, and just includes a line number. But I'm not sure that there's anything we can do to mitigate that, other than to keep the JS readable...
I find I don't often need to look at the compiled JavaScript very often, but it is perfectly readable to do so if required.