
CoffeeScript, a little language that compiles to JavaScript. Happy Holidays, HN - jashkenas
http://jashkenas.github.com/coffee-script/
======
jashkenas
JavaScript has always had a gorgeous object model hidden within Java-esque
syntax. CoffeeScript is an attempt to expose the good parts of JavaScript
through syntax that favors expressions over statements, cuts down on
punctuation noise, and provides pretty function literals. This CoffeeScript:

    
    
        square: x => x * x.
    

Compiles into this JavaScript:

    
    
        var square = function(x) {
          return x * x;
        };
    

If anyone has specific ideas about aspects of JavaScript that they think could
be more convenient or better-looking, I'd love to hear them. Cheers.

~~~
j_baker
Alright, this is bugging me. Why are you using colons for assignment?

~~~
jrockway
I am not the author, but I assume because it's nice-looking, and JavaScript
already uses it for assignment (inconsistently). Consider:

    
    
       var foo = 42;
    
       { foo: 42 }
    

Why make the two concepts different?

Also, it's "binding", not assignment.

~~~
jashkenas

        a: 1
        b: 2
    
        obj: {
          a: 1
          b: 2
        }
    

Quite right, and internally, both kinds of assignment compile into a
CoffeeScript AssignNode. The latter is just tagged as occurring within an
object literal. Having the commas be optional in multiline objects also helps
make both kinds of assignment look identical.

(And I'm calling it assignment because saying "binding" in JavaScript usually
means that you're binding a function to a "this" context object)

~~~
nir
Any chance you'll allow equal sign as well as colon? For all of us coders
conditioned by years of programming to automatically use "="...

Looks beautiful, in any case.

~~~
jashkenas
Sure, why not. They can be interchangeable. It's in the latest commit, and
will go out with the next release.

~~~
nir
Cool, thanks!

------
tlrobinson
Very cool.

I've created a simple CommonJS package that lets you use CoffeeScript on the
command line with Narwhal: <http://github.com/tlrobinson/coffee-script>

If you have Narwhal (<http://narwhaljs.org/>) just install with the package
manager "tusk install coffee-script", then you can create Narwhal modules
ending in .cs, or run the command line REPL "cs"

(there are a few problems, such as variables in the REPL are created in a
local scope, etc, but this was literally a 15 minute hack, I'll improve it
when I have time, or feel free to submit patches)

I'm starting to think of Narwhal as less of a JavaScript platform and more of
a VM akin to the JVM... JSVM? It now supports Objective-J ("tusk install
objective-j" or "cappuccino"), a proof of concept Ruby implementation ("tusk
install cappruby"), and now CoffeeScript.

~~~
jashkenas
Oh wow. A CoffeeScript REPL? If it can hook right in to the Narwhal standard
library, that'll be quite a thing to see.

Looking briefly at your commits, I'll add a flag to disable the function
safety wrapper so that you don't need to strip it off. Thanks for the tusk
package -- it's quite a Christmas present.

~~~
tlrobinson
Indeed it can use all the standard library modules, since the normal "require"
functions are available.

The other problem with the REPL is that all variable assignments are prefixed
with "var", so they're always local to the function where the eval-ing is
done, thus it's impossible to get variables to persist between REPL commands
(without explicitly assigning to a property of "global").

One solution would be to add a flag that disables the "var" for top level
assignments (but not within functions).

Alternatively, I think I could do some engine-specific black magic (__proto__
hacks), at least for Rhino.

~~~
jashkenas
Alright -- this is fixed with the latest commit. I've enhanced the --no-wrap
option to not emit "var" declarations at the top level. If you don't want the
safety wrapper, then you probably intend to make them global in any case.

bin/cs now works like a charm. I might pull it back into the main bin/coffee-
script executable. Start a REPL if run without arguments ... --run for
executing CoffeeScripts via Narwhal... something like that.

~~~
tlrobinson
On second thought, ideally these should be different options. When used in a
module we add our own function wrapper but still want the "var"s. In the REPL
we don't want the "var"s (making the safety wrapper useless, but harmless)

------
tolmasky
I believe there is a bug in your object model for handling super. I have
tested it and it does indeed work incorrectly (although not in the way I
expected it to work incorrectly). Basically, your super calls only work if
your class tree is 1 level deep. As soon as you have something like Animal ->
Horse -> BigHorse, then it breaks down. This is because super() gets
translated to:

this.constructor.prototype.NAME.call(this, args)

So, if we had move on BigHorse, Horse, and Animal, BigHorse would call super
(resulting in Horse's implementation being called), but then Horse calls super
which _again_ calls Horse's implementation, since this.constructor.prototype
still points to Horse.prototype. Thus, you can't continue climbing up the
chain. When I tried it though, it seemed to just skip the intermediate classes
and go down to the base class, which is of course equally broken. We used to
have the same problem a long time ago in Objective-J. The fix is pretty
simple, you should just inline the actual class name when dealing with supers:

Horse.prototype.NAME.call(this, args), which then calls:
Animal.prototype.NAME.call(this, args)

Hope I avoided you some pain with that one. I remember 3 years ago not
understanding why on earth my code was broken and thinking it was algorithmic
for the longest time until I realized the entire underlying parts were broken.

~~~
jashkenas
Thanks for that. You're absolutely right -- the javascript dynamically-scoped
"this" keyword strikes again. I've fixed it in the last commit, by using
prototype.__proto__, and adding an "extends" keyword for convenience. You can
see it in action here:

<http://jashkenas.github.com/coffee-script/#inheritance>

It's out with version 0.1.2 now.

~~~
tlrobinson
Be careful with __proto__, it's non-standard and will break in some engines.

~~~
vr
To get the equivalent of __proto__ that works in every engine see
goog.inherits from Closure.

~~~
jashkenas
Thanks for the pointer. I've pushed a commit that avoids __proto__, and
produces this...

CoffeeScript:

    
    
        Horse extends Animal
    

JavaScript:

    
    
        Horse.__superClass__ = Animal.prototype;
        Horse.prototype = new Animal();
        Horse.prototype.constructor = Horse;
    

Calling super uses the __superClass__ reference. I hate to add it as an
enumerable property, but there doesn't seem to be any other way to get at it,
and at least it's on the class and not the object.

------
cscotta
Wow, this is a really nice little language.

The howto refreshing to read. While I love JS as a language, even written
longhand, it feels like you've brought an uncommon elegance to it. The syntax
is much simpler and maps more cleanly to how I think. It feels a lot like
Ruby.

Granted, most of the JS I work on is in pretty complex applications, so I'd be
hesitant to work in anything but the target language itself. But it's a very
clean abstraction or concept to begin with. I'd love to see something complex
written in CoffeeScript, especially with DOM manipulation or another JS
framework involved.

It'd be fun to try writing a DSL or simple compiler sometime. This must have
been very interesting to create. Cheers for such detailed examples in the
unveiling as well.

Great job, and thanks again!

Edit: Hahaha, thanks for the examples from the Poignant Guide. Pouring out a
few drops for _why right now.

Edit 2: Forgot that you wrote underscore.js - thanks for that as well. Crazy
to see how simple it is when written in CS.

~~~
jashkenas
I completely agree -- I'm not about to start using it for real projects, it's
really just a thought experiment about how nice JavaScript could be with an
alternative syntax.

That said, if you work with Ruby and want to play with compilers, check out
CoffeeScript's source. It's got a clean Ruby lexer and Racc parser (examples
of which are hard to find in the wild) -- the code generation is a little
funky, but I'm hoping to clean that up. The whole shebang is under 1500 LOC,
including comments, so it's not too much to wrap your head around.

------
amix
CoffeeScript + node.js + V8 could produce one amazing language and
implementation. Great job on the syntax, I think it's cleaner and more
readable than JavaScript.

~~~
tlrobinson
It already works on Narwhal (<http://narwhaljs.org>). See
<http://news.ycombinator.com/item?id=1014508>

------
statictype
Very nice. How about language-level support for asynchronous operations (like
the async monad in F#)

    
    
        async update_table:q =>
            r1,err = xhr('http://somewhere?q='+q)
            if err then
                $('error').innerHTML = err
                return.
            r2,err = xhr('http://somewhere-else?q='+r1)
    
            if err then
                $('error').innerHTML = err
                return.
    
            $('result').innerHTML = r2
            .
    

could be the equivalent of something like:

    
    
        function update_table(q) {
            var on_error = function(err) { $('error').innerHTML = err; }
            
            doXHR('http://somewhere?q='+q
                ,function(r1){
                    doXHR('http://somewhere-else?q='+r1,function(r2){
                        $('result').innerHTML = r2;
                    },err));
                },err);
            );
            
        }

------
extension
Splendid! This is what JS really needs, a thin layer to clean up the syntax
and patch the quirks, without breaking anything. I made the same kind of thing
using the actual Ruby parser:

<http://github.com/jedediah/prettyscript>

It's only barely usable at this point and I've been too busy to work on it.

~~~
jashkenas
Great stuff. The one really interesting overlap between the two -- from taking
a quick glance at your readme, is that we're both trying to convert things
that otherwise would be statements in JavaScript into expressions.

CoffeeScript does this by using the AST to recursively push down returns and
assignments requested from outside a block to the final line of each possible
branch of execution:

<http://jashkenas.github.com/coffee-script/#expressions>

How are you doing it? Did you find any particularly tricky cases?

~~~
extension
That's pretty much what I did. There is a polymorphic method to_js_tail on AST
nodes that asks the node to emit itself as a return statement. Nodes that are
values just prepend "return", blocks forward the call to their last statement,
control structures forward to each of their blocks and so on.

That just handles returns. I never implemented statements as expressions in
general. I was planning to do that by wrapping non-expressions in anonymous
functions but if you can do it by injecting temp variables, that's probably a
lot more efficient.

------
jrockway
This is nice. There are too many gotchas to remember in JavaScript; better to
let a liberal compiler translate something nice into the "machine code" that
works in all browsers.

(Personally, I always write:

    
    
       something = {
           foo: "bar",
           bar: "baz",
       }
    

which works in Firefox, but not IE. A compiler would not be upset when it
omits that extra trailing comma. Of course, you can always write:

    
    
        something = { foo: "bar"
                    , bar: "baz"
                    }
    

But let's face it, that is only not ugly in Haskell.)

~~~
jashkenas
Which is why CoffeeScript lets you leave commas out of multiline object and
array literals (for those that didn't see catch it in there):

    
    
        something: {
          foo: "bar"
          bar: "baz"
        }

------
leif
Beautiful!

(I finally made it to the end of the article where you ask about block
delimiting...)

I think there are two issues, one being that '........' at the end of a large
nested expression is ugly, the other being that it's hard to find the start
and end of a block as it stands.

Since you seem to borrow a lot from ruby, why not add {} and/or begin/end as
options? I would definitely keep the period syntax around (for short, shallow
blocks), rather than replacing it.

"aint"? Cute.

~~~
jashkenas
Yeah, the periods to end block scope are the part of the syntax I'm the least
happy with. I couldn't think of anything nicer without resorting to
significant whitespace (which I'd like to avoid).

I'd rather not add {/}, or begin/end, because it's easy to determine the
beginning of the block, it's just closing it that we need a symbol for.

In terms of style, you can certainly indent the period on it's own line if you
prefer:

    
    
        elements.each(el =>
          $(el).click(event =>
            if el.hidden
              el.highlight()
            .
          .)
        .)
    

But I'd love to hear more suggestions for alternatives.

~~~
tdavis
Out of curiosity, what makes you want to avoid significant whitespace? I think
it solves the issue quite nicely. For what it's worth, I used to think SW was
completely absurd. Then I spent a few years actually writing Python and have
no desire to ever go back.

~~~
jashkenas
I like to have the flexibility to indent in whatever style the code calls for.
That said, looking over the Python docs again, their implementation of
significant whitespace is a lot more flexible than I remembered. I didn't
realize you could do one-line method definitions, and that the whitespace only
kicks in when you write a multi-line block.

Here's a question for you. In Python, if you have a couple of nested lambdas
with multiline bodies, getting passed into each other as arguments, do you
have to write the closing parenthesis on the correctly-indented line on the
other side, or can it be tucked against the inner lambda?

In a hypothetical significant-whitespace CoffeeScript, I'm thinking of this:

    
    
        elements.each(el =>
          el.click(event =>
            el.show()))
    

Versus this:

    
    
        elements.each(el =>
          el.click(event =>
            el.show()
          )
        )
    

Is the second form required to make Python-style whitespace work?

~~~
aaronsw
No.

~~~
jashkenas
Great. Then let's start a "whitespace" branch, and give it a go.

Edit: Alright, got a branch that starts with a little significant whitespace
going here:

<http://github.com/jashkenas/coffee-script/tree/whitespace>

It can't handle the first of the two cases mentioned above just yet, but it
compiles the second one correctly.

------
jcl
Heh... Might want to choose a file extension other than ".cs".

~~~
jashkenas
Yeah, all the good extensions are taken -- .cof and .cfs are already other
things as well. None of the tooling in the compiler cares about what extension
you use: you can call it .coffeescript if you'd like. I'm going to stick to
.cs for the parallel to .js for the time being.

~~~
jeremyw
.coffee seems an appropriate balance between overloading the namespace and a
too-long common string in one's directory.

The 2- and 3-letter extension tyranny must stopped!

~~~
jashkenas
Nice idea. I've changed the official extension from ".cs" to ".coffee", and
it'll go out with the next release. Thanks.

------
tlrobinson
How do you do object literals with non-indentifier characters in property
names? (e.x. { "foo+bar" : "baz" } in JS)

~~~
jashkenas
You couldn't, a minute ago. I've just pushed an update to the grammar that
allows you to use quoted strings inside of object literals.

Thanks for catching it. I really appreciate folks taking the time to find the
little missing pieces.

------
wmoxam
Very cool, the syntax is similar to Potion

CoffeeScript:

    
    
      square: x => x * x.
    

Potion:

    
    
      square = (x): x * y.

------
mbenjaminsmith
Cool. I've been dreaming of writing a similar python => javascript (which
someone has probably done already).

My only worry about something like this is the extra step of compiling, though
I run a script that compresses via YUI comp. anyway...

Thanks for sharing.

~~~
JeffJenkins
You're looking for this: <http://pyjs.org/>

I had written a mostly functional python->JS translator using the compiler and
compiler.ast packages, but I suspect this is more complete and actively
developed.

My package is here if you wanted something stand-alone (but only 80% complete)
to play around with: <http://github.com/jeffjenkins/py2js>

------
wensing
That array comprehension feature is hot.

------
jashkenas
CoffeeScript's now up to 0.1.3, which includes --interactive to launch a
CoffeeScript REPL, and --run to compile and execute scripts in a single pass.
Both options depend on a working Narwhal installation.

~~~
Schtarflucz
Sweet!

Now, add a minifying pass after compilation for deployment and you'll have
created a dream platform for client-side scripting (-:

edit: another suggestion since one of your goals is terseness: you may want to
alias "prototype" to "proto".

edit2: as much as I like the column for asignment (present in both JS itself
and Ruby 1.9), I'm not really fan of the +:, -:, etc. combined operators, at
least in a math context. &&: and ||: look fine though so it's perhaps a matter
of getting used to it.

edit3: I just discover your underscore.js library. Do you plan to include it
in the CoffeScript standard lib?

edit4: oh... And I hope you like the color of this brand new bikeshed I just
built in front of your coffe shop ;-)

~~~
jashkenas
Yeah, I think you're right about the other operators -- I've just pushed a
commit that puts them back to +=, -=, ||=, and so on. Thanks.

Underscore is going to stay unrelated, but there's no reason that you couldn't
use it from CoffeeScript, to great effect.

------
karanbhangui
probably irrelevant, but very pleasing design on that page :D

------
collint
I like the look of this. I'm excited about the possibilities of languages that
target JavaScript. It's quite a shame that the "open" web has only one
language.

------
Daishiman
Looks great! Does it work with jQuery?

------
aboodman
This is really cool. Nice work.

