CoffeeScript is a simple, clean, fast language which compiles to JavaScript, either at build time, with a caching framework plugin on the server, or at runtime in the browser.
The syntax looks like a cross between Python and Haskell1 and, generally speaking, is used similarly enough to Python that you’ll have little trouble picking it up and using it comfortably.
It does have a few of what non-Ruby programmers will consider distasteful warts, but for the most part, it’s an elegant and delightful language.
You’ll probably want to keep the syntax reference open in a background tab at first, but if you’re comfortable with Python and you’ve done any programming with jQuery or some other modern JavaScript library, you’ll feel right at home.
Below, I’ve summarized the points which the documentation doesn’t completely prepare you for as a Python programmer.
Helpful features with no direct Python analogue
A.K.A. Things which you’ll probably forget to use at first, but which you really should work to remember.
- Use almost anything as an expression
- Not only does CoffeeScript have anonymous functions, you can treat pretty much anything as an expression and it’ll be wrapped in an anonymous function if needed.
- Indent-stripping multi-line strings
- If you use single-quotes rather than triple-quotes for multi-line strings, it’ll still work and leading indentation common to all lines will be stripped. (like
textwrap.dedent()
, if you’re familiar with it) - Embedding expressions in strings
- Given how much string templating is done in JavaScript, it really helps that you can put any expression directly inside the Ruby-style
#{string interpolation operator}
for own key of
- To iterate over only the properties which weren’t inherited, use
for own key of
rather thanfor key of
. (Very useful since JavaScript doesn’t make a distinction between properties and associative array keys) - Fat-arrow function syntax
- To bind
this
to the parent object, even in a callback, simply define it with=>
rather than->
- Ultra-concise constructors
- Rather than filling your class constructors with
this.foo = foo
, you can simply use the@
shorthand forthis.
and writeconstructor: (@foo, @bar) ->
with an empty method body. - Checking for the existence of variables and properties is concise.
- Use
variable ? "default value"
andobj.method?().thing?.widget
Similarities and differences with Python not explicitly mentioned
A.K.A. What the documentation won’t directly prepare you for if you have Python instincts.
- Ternary syntax will break your muscle memory
- For a ternary expression, Python uses
result = a if test else b
while CoffeeScript usesresult = if test then a else b
. (Theif
statement syntax on a single line) a = b and c or d
works Pythonically but you don’t need backwards-compatibility with pre-ternary Python releases- Boolean operators don’t coerce the return value to a boolean (a will be c or d, not true or false) but 99% of the time, what you really want is the ternary operator.
- Comprehensions use
when
, notif
- If you write
result = (x for x in list if x % 2 == 0)
, you won’t get an error… but you will get a loop inside a conditional rather than a conditional inside a loop.
(Ifitem
in the parent scope isn’t evenly divisible by 2, the loop will be skipped. If it is,result
will be identical tolist
) - Comprehensions iterating two lists produce a two-dimensional array
- In Python, this syntax will produce a one-dimensional list while, in CoffeeScript, it produces a list of lists.
a+b for a in A for b in B
newList = list1 + list2 + list3
is not array concatenation- You could use
newList = [].concat list1, list2, list3
instead, but a cleaner and more flexible alternative is to use splats. This example cleanly concatenates three lists and two individual numbers into a single list:newList = [listA..., numA, numB, listB..., listC...]
In my opinion, this makes CoffeeScript cleaner and more intuitive than Python for this task.
- When
foo
is an array,if foo
is always true [] != false
so, to check whether a list is empty, you have to doif foo.length
instead.someFunction(arg3 = 1)
is not the syntax for positional arguments- …but because assignment is valid in an expression, it won’t raise an error. What you want is
someFunction null, null, 1
orsomeFunction(null, null, 1)
- There is an equivalent to
someFunction(x, y, *args)
- As in Python, splats aren’t just for function definitions.
someFunction x, y, args...
Counter-intuitive Perl/PHP/Ruby/etc.-isms
A.K.A. Where most of your subtle, hard-to-find bugs will come from as a Python programmer.
- If you don’t use parentheses in a function call, CoffeeScript will guess them for you
- …but Haskell programmers and shell scripters will be surprised when
a b c d
meansa(b(c(d)))
rather thana(b,c,d)
. This also means thatfoo ()
is sometimes invalid whenfoo()
is OK. - On the plus side, it works very well for ensuring that anonymously defined callbacks aren’t an exception to the “indents, not braces” block syntax.
- The rules are simple but it’ll still take some getting used to before I’ll stop occasionally tripping over them. Here’s how jashkenas explained it to me:
The call wraps forwards to the end of the line, or to the end of the indented block, with one specific exception for postfix conditionals.
alert a alert(a) alert inspect a alert(inspect(a)) alert inspect a if b alert(inspect(a)) if b
- Colons aren’t part of the block syntax
- Habitually typing a colon after a function name or control statement will turn it into an object property (think dict literals)… which can do all manner of crazy things if it’s something like “else:” which is still valid CoffeeScript.
- You can’t shadow a higher-level variable
- …only refer to it as in Ruby’s “local scope”, so expect to cause subtle bugs if you habitually reuse a handful of temporary variable names. If you want to get out of the habit, PyLint‘s default configuration will complain when you do this in your Python code.
- Like Perl and Ruby and unlike JavaScript, CoffeeScript does implicit returns
- …so expect subtle bugs until you internalize that CoffeeScript’s function syntax is really a multi-line lambda.
(As the docs say when talking about comprehensions intended only for their side-effects, “Be careful that you’re not accidentally returning the results of the comprehension in these cases, by adding a meaningful return value, like true, or null, to the bottom of your function.”) - As with PHP, single- and double-quoted strings have different meanings
- …with interpolation only working in double-quoted strings.
- Like in C and many C-inspired syntaxes, and explicitly unlike Python, you can do assignment inside an expression.
- So expect to shoot yourself in the foot on occasion by accidentally typing
=
instead of==
and not getting an error message. (You can, however, reduce the risks by habituating yourself to theis
andisnt
aliases for==
and!=
)
Caveats to re-mention for JavaScript programmers
A.K.A. Where subtle, hard-to-find bugs may come from if you have a lot of experience working with plain old JavaScript.
- Switch statements don’t allow you to fall-through to the next case
- CoffeeScript automatically inserts
break;
into every case. However,when
statements will accept comma-separated lists instead. (I seem to have misplaced the response which gave me this tip. Help appreciated in tracking it down so I can link it.) - To avoid namespace pollution, everything is run in an anonymous wrapper
- …so you have to explicitly attach things to the
window
object if that’s what you want. ==
and!=
are converted into===
and!==
- If you really want the intransitive, coercion-inducing version, wrap the expression in backticks to mark it as raw, untranslated JavaScript.
- CoffeeScript’s
in
is Python’sin
- JavaScript’s
in
is CoffeeScript’sof
.
Python features still to find equivalents for
- Concise
any(iterable)
,all(iterable)
, andreduce(function, sequence)
without relying on Underscore.coffee or ECMAScript 5.
Did I miss something?
I’m new to CoffeeScript, so please let me know if I’m wrong or if I forgot to mention some area where Python instincts will steer you wrong with CoffeeScript.
Also, don’t forget to read the FAQ. It’s not as noticeable as the docs, but it’s almost as useful. 🙂
1. Technically, the syntax is inspired by Ruby and YAML, but I know quite a few Python programmers for whom that wouldn’t mean anything, so I compare to Python and Haskell first instead.
A Python programmer’s first impression of CoffeeScript by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Excellent article. CoffeeScript has been getting a ton of attention from the Ruby community, yet oddly doesn’t seem to have taken hold among Pythonistas. (Which is weird, since significant whitespace seems to be the only big difference between Ruby and Python these days.)
Anyway, just wanted to mention that I have a new book out on CoffeeScript, published by PragProg: http://pragprog.com/titles/tbcoffee/coffeescript
There’s a bit more difference than that.
I often hear people who use both Python and Ruby saying things that boil down to “I wish Python had an anonymous block syntax like Ruby so it could be better in every way”, so it’s fair to say that Ruby has a significant advantage over Python when it comes to creating clever DSLs.
On the other hand, I also hear people saying that Ruby’s syntax seems inelegant and Perl-like to a mind accustomed to Python and that there are no Ruby-programming jobs out there, only Rails jobs… which cripples the language’s uptake, regardless of how truthful it turns out to be.
I can see various bits of Ruby’s influence in CoffeeScript, which would explain some distaste from Python users, but given how much closer to Python it is than Javascript, that should be dwarfed by the benefits, so I don’t understand why it hasn’t taken off like a rocket in the Python community either.
Python is my language of choice (I’m not a pro, just a hobbiest, but this is the only language I know well). I really like its syntax and I’ve never felt comfortable with c-like languages like php, c#, javascript, etc. So as you can imagine, javascript for the client side has always been a pain in the neck for me, which I tried to avoid as much as possible.
However, after a few minutes of playing with Coffeescript I can say that I like it a lot. I t has some features taken from Ruby (I guess) but they don’t look weird to me at all.
In some aspects, I even like it more than python itself.
I’ve been playing with it, integrating it with jquery, and the whole experience has been very nice.
So there’s nothing wrong with this language from a pythonist point of view (at least mine).
I guess it doesn’t enjoy the same level of popularity than within the ruby comunity simply because it doesn’t have a “rails” supporting it. That’s it.
“So expect to shoot yourself in the foot on occasion by accidentally typing = instead of == and not getting an error message.”
My Tip: use “is” and “isnt” for “==” and “=!”, also: “||” -> “or” and “&&” -> “and”
That’s not really a solution since, in Python, is and == have different meanings and, therefore, experienced Python programmers will habitually type == when they want strict equality. (In Python, “is” tests whether two references point to the same in-memory object)
As for “and” and “or”, I don’t see how that helps. In Python, and/or are valid and &&/|| are not (so they’ll be what Python programmers habitually type) and, in JavaScript, && /|| are valid and and/or are not, so it’s actually the opposite of what you said. (and/or are aliases for &&/|| in CoffeeScript, not the other way around)
Thanks for commenting though.
I think my sentence was not clear, i meant that you should use “and” and “or”, if you use Coffeescript instead of “||” and “&&” cause the same type of error can occur with these too: | and & are as valid as = in an expression is(in JS)
i did not know that in Python “==” and “is” makes a difference, so thank you for making that clear 🙂
Ahh. I agree, but since this is targeted at Python programmers who may also have Javascript experience, I’m not really sure it’s appropriate to talk about “&&” and “||”. I think it’d dilute the points I’m making.
Yes, they can easily be typo’d into the bitwise and/or operators, but that’s more the kind of mistake you’d get from a Java/JS/C/C++/PHP programmer than a Python programmer, even if they use JS frequently.
If you really do want the coercion-inducing ==, you can use “ to escape back into regular JavaScript.
doSomething() if `a == “coerce”`
For some reason, it hadn’t occurred to me that the escape syntax could be used with that level of precision. Thanks.
I’m a newcomer to CoffeeScript, but in my experiments it seems like the escape syntax works around any well-formed expression.
Great post! One minor nit pick. It looks like in this line you are using “new” as a variable name, which is a collision with the “new” keyword. Just “newList” will clear it up.
new = [].concat list1, list2, list3
Thanks. Wasn’t thinking there. If I can ever get around to switching my blog over Jekyll or Blogofile, I’m actually planning to hook in some kind of code snippet validation.
Maybe a custom Blogofile pre-filter which runs a validator and then outputs the normal markdown code snippet syntax.
Who knows. If my productivity gains continue, maybe it’ll be done half-way to the heat death of the universe. 😛
One thing which I didn’t realize until I saw it used (which took a while… there are a lot more javascript snippets out there than coffeescript ones) was that the implicit parentheses really do work for blocks. As a pythonista, I was biased against that implicit magic until I saw that:
someobject.bind somevent,->
dosomething()
is much prettier than
someobject.bind(somevent,-> dosomething()
)
The same trick works when you need to pass a params object.
Both the coffescript docs and this blog just rush across this “or the end of the block” wrapping for parentheses, which is to me the most important part.
For someFunction(x, y, *args) you can do
someFunction( x, y, args… )
Ok, for that one, I’m just an idiot. There’s no excuse for me not having checked whether splats could be used on both sides of a function call. Fixed.
Good point. I’ve added a quick mention to the post with a link to your comment.
> You can’t shadow a higher-level variable
It’s a pity that new script languages still like to mess around the rules of variable scoping that have served us so well for more than a hundred year now (mathematics quantifiers). I wish well for CoffeeScript, but direly regret that it’s one more language making that monumental mistake by trying too hard to innovate on that front. Please, give me a local variable!
> You can’t shadow a higher-level variable
It’s a pity that new script languages still like to mess around the rules of variable scoping that have served us so well for more than a hundred year now (mathematics quantifiers). I wish well for CoffeeScript, but direly regret that it’s one more language making that monumental mistake by trying too hard to innovate on that front. Please, give me a local variable!
A minor nitpick, you say: “but Haskell programmers […] will be surprised when a b c d means a(b(c(d))) rather than a(b,c,d). ”
but in Haskell, a b c d does mean a (b (c d))) and not a(b,c,d)
Perhaps. I’m still very much a Haskell newbie and, for all I know, it could be an artifact of some quirk of Haskell I don’t fully understand yet.
All I know is that, in Haskell, `foo x = bar x y` means that “foo x” should return “bar(x,y)” while, in CoffeeScript, it’s “bar(x(y))”.
I just double-checked several Haskell tutorials and re-checked the CoffeeScript interactive compiler on that point.
Actually, it means ((bar x) y).
🙂
What I mean is that, because JavaScript’s dynamic typing makes static analysis extremely difficult, CoffeeScript isn’t smart enough to know that “func1 int1 int2” shouldn’t be “func1(int1(int2))”.
Haskell, on the other hand, is statically (though implicitly) typed. It can easily make function application left-associative, so “func1 int1 int2” means “func1(int1, int2)” while “func1 func2 int1” means “func1(func2(int1))”.
If anyone would like to argue THAT point, please do it somewhere it has a chance to make a difference to the world and talk to the authors of Real World Haskell, Learn You A Haskell for Great Good, and the Haskell WikiBook.
I do, however, admit that my example variable names could’ve made that more obvious.
Update: Weeble may be correct. It’s been a long time since I started playing around with Haskell and it’s possible I misunderstood what was going on. Unfortunately, I don’t have time to check that at the moment, so let’s just say this comment’s accuracy is in doubt.
As I understand it, that’s not why this works in Haskell. It doesn’t attempt to parse the function call different ways until it finds one that type-checks correctly. The reason it works is that in Haskell, *all* functions take one argument and return one argument. When you declare a function like this:
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
…you are in fact declaring that addThree is a function that takes a parameter x and returns {a function that takes a parameter y and returns {a function that takes a parameter z and returns x+y+z} }. See also http://en.wikipedia.org/wiki/Currying
So with that in mind, addThree foo bar baz will be parsed as (((addThree foo) bar) baz) and with a little thought you can see that it all works out. Since Javascript does not treat multi-argument functions as single-argument functions that return functions, Coffeescript can’t really benefit from this syntax, even though it treats it much the same as Haskell does.
Specific points:
@Zorg I think your example has the parentheses the wrong way round. a b c d is equivalent to ((a b) c) d, not a (b (c d)).
@ssokolow I don’t think Haskell will interpret “func1 func2 int1” from your example as you expect. I think it will parse it as “(func1 func2) int” and complain that func1 expects an int and not a function as input. I don’t have it installed right now to check, though.
A Haskell function like:
f x y = x+y
(f 3 4 reduces to 7)
has the type int -> int -> int because Haskell uses currying everywhere.
so f 3 4 = (f(3(4)))
http://en.wikipedia.org/wiki/Currying
Pingback: Links for 2011-05-14 — Business Developer Talk
Pingback: Peters Linkschleuder – Der Schockwellenreiter
One thing which I have been bitten by is that adding an extra : at the end of an if or class statement, as my python instincts would suggest, is NOT a syntax error, but does something I definitely don’t want.
I’ve found another difference concerning List Comprehensions. In Python
def cross(A, B):
return [a+b for a in A for b in B]returns an one-dimensional array if you call it with two arrays (or strings). But in CoffeeScriptcross = (A, B) -> (a+b for a in A for b in B)returns a two-dimensional array.http://alicebobandmallory.com/articles/2011/04/19/sudoku-solver-in-coffeescript
Good catch. I can never remember to use that kind of comprehension when it’s useful, so I’m not sure whether that difference would ever bite me, but I know I’d never notice. I’ve added it to the post. (Citing your comment rather than your post to give you proper credit for bringing it to my attention)
Also, thanks for linking to Trevor Burnham’s article in your own comments. It does a very nice job of being “Why CoffeeScript: Javascript Programmer Edition” so I’ll link it here.
http://pragprog.com/magazines/2011-05/a-coffeescript-intervention
just an FYI, I ran across one more thing. I use enumerate CONSTANTLY for lists in python. To replicate the same in coffeescript, instead of
for k, v in enumerate(mylist):
dosomething(k, v)
in coffeescript:
for v, k in mylist
dosomething(k, v)
I thought it was helpful, and didn’t see it above or in the comments.
I use it less often, which explains why it didn’t occur to me, but you’re right.
Pingback: First Look at Python | Small Dropbear
Python code :
name = “kracekumar”
age = 21
print name + age
Ouput:Traceback (most recent call last):
Line 3, in
print name + age
TypeError: cannot concatenate ‘str’ and ‘int’ objects
Same Logic in coffeescriptadd = (a, b) -> a + balert add ” west “, 2Output :Modal window displaying “west 2” (without quotes)
Pingback: 8 PCs In A Bunker: Ownership, Respect, and Principles in the Steam Era | Stephan Sokolow’s Blog
Having a mixed up syntax just makes programming difficult using CoffeeScript. I still prefer the Python syntax and plan on using Python Fiddle to build my next project.
Ed.: This person appears to be the author of PythonFiddle.
Different languages have different strengths and, as someone who’s used to using Python for web apps, let me say that Python compiled to Javascript is not the best solution for client-side creations, even with jQuery. (And, while I haven’t had time to get personal experience with both of them yet, I get the impression that Node.js is more intuitive than Twisted as a way to back a COMET or WebSocket-based real-time application, even without helpers like Socket.io.)
It may have its rough edges, but CoffeeScript has a bunch of features that aren’t replicated in that Python converter’s modified syntax as far as I can tell. For example, Indent-stripping multi-line strings and the choice between thin arrow and fat arrow function syntax. (As mentioned in the above post)
Heck, you could make the same “mixed-up syntax” jab at Python for combining so many elements from imperative, object-oriented, and functional languages. I can write a horrendous program in Python just as easily as in CoffeeScript. The only difference is that CoffeeScript syntax is a little looser.
Also, most importantly, the purpose of a language is to be easy to read and write. From all the provided examples, the dialect of Python implemented in PythonFiddle looks closer to JavaScript+jQuery than CoffeeScript+jQuery in readability, which means it makes things HARDER to read than either server-side Python OR client-side CoffeeScript.
It just doesn’t really feel like it brings anything to the job that’s worth the pain further down the road when you’re inevitably forced to go under the hood because of a leaky abstraction. CoffeeScript is closer to Zen Coding for Javascript (I assume you like Zen Coding, since PythonFiddle seems to use it) than Python-to-Javascript translation is, partly because it’s designed to be that way, but mostly because you’re not bringing over a bunch of expectations from your time working with CPython.
It also doesn’t help that you’re comparing an IDE to a language in a way that makes me wonder if you wrote PythonFiddle and are just trying to get some free publicity for it. CoffeeScript, when written in something like Cloud9 IDE or a well-tuned copy of Vim or Emacs is at least as comfortable as PythonFiddle. There’s a BIG difference between a JSFiddle clone and a proper IDE.
It also really gives a bad impression that the “Click the logo for an explanation of what this site’s about” message is lying and clicking the logo really just switches between two different layouts with no explanation of why the site even has two different layouts.
Thanks for the interesting article! Just a minor point, but I thought you might find this interesting:
Instead of
newList = [].concat list1, list2, list3
you can also write
[list1..., list2..., list3...]
(Splats in an array literal, that is.) I find this slightly preferable to the awkward concat syntax.
Hello Stephan,
Nice article. I have a copy of it that I used to glance at everytime I wanted to try out something new with CoffeeScript.
I wanted to let you know that I’ve succesfully put together a Node.js-less Pythonic CoffeeScript compiler using PyV8 as the interface between Python and JavaScript and PyYAML to give sort of a joy when it comes to continuous building or file-driven build configuration.
Perhaps it is of your interest.
It’s named Expresso, and it’s git repo is over here.
Have a nice day!
Glad I could help.
I’ll keep an eye on Expresso. I don’t have a need for it right this instant, but it looks like an interesting idea.
Take care here:
is not
is notisnt
.not
has a very low precedence in Python, only aboveand
andor
. In CoffeeScript it is a synonym for!
, with high precedence, which doesn’t exist in Python.while not a < 0
has different meanings in both languages. In CoffeeScript, you better useunless
here. Very unfortunate, in my view.JavaScript allows to use local variables (use
var
), or elevate to the next level. – I asked myself, why it doesn’t use it the other way around: no keyword means local, a keyword (maybeexport
?) takes the other scope. – CoffeeScript messed it completely up here. Very unfortunate. In CoffeeScript you don’t know what you get, you have to search in the environment if you used that name already…In Python it’s not perfect either: no keywords is local (great), but
global
goes directly top-level! – The use ofwindow
in CoffeeScript to go global is a (good) excuse: when the host environment doesn’t sets a name for the global scope, you have to do it yourself then… (window = this
somewhere top-level)sorry… “take the outer scope”, not the “other scope”…
CoffeeScript probably didn’t want to deviate from JavaScript too much, in which local variables are declared with `var` and global scope is declared without.
Given that the developer is consistent and avoids declaring global variables except in the global scope (if even then… see [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection)) I actually prefer that `var` is used because it’s clearer when a new variable is introduced. See Google’s [JavaScript Style Guide] https://google.github.io/styleguide/javascriptguide.xml#var) for example.
I do wish it wasn’t allowed to reassign a variable to another type without explicitly typing `var` again though.
About your question:
“Concise any(iterable), all(iterable), and reduce(function, sequence)”
I think this could be useful:
http://ricardo.cc/2011/06/02/10-CoffeeScript-One-Liners-to-Impress-Your-Friends.html
”
[1..1000].reduce (t, s) -> t + s
Math.max.apply @, [14, 35, -7, 46, 98] # 98
Math.min.apply @, [14, 35, -7, 46, 98] # -7
“
Good point with
Math.min.apply
andMath.max.apply
but, unfortunately, thatreduce
isn’t good enough.(That “Assume node.js as the environment for all examples.” line should really be bold.)
Having reduce as part of the prototype for arrays is a feature of ECMAScript 5, which means that Internet Explorer 8 and below don’t have it.
It’s useful on Node.js because you have control of the execution environment but, unless you’re writing something which is already incompatible with IE8 for other reasons, it’s not a viable client-side option.