A Python programmer’s first impression of CoffeeScript

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 than for 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 for this. and write constructor: (@foo, @bar) -> with an empty method body.
Checking for the existence of variables and properties is concise.
Use variable ? "default value" and obj.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  uses result = if test then a else b. (The if 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, not if
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.
(If item in the parent scope isn’t evenly divisible by 2, the loop will be skipped. If it is, result will be identical to list)
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 do if 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 or someFunction(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 means a(b(c(d))) rather than a(b,c,d). This also means that foo () is sometimes invalid when foo() 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 the is and isnt 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’s in
JavaScript’s in is CoffeeScript’s of.

Python features still to find equivalents for

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.

CC BY-SA 4.0 A Python programmer’s first impression of CoffeeScript by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

This entry was posted in Geek Stuff. Bookmark the permalink.

45 Responses to A Python programmer’s first impression of CoffeeScript

  1. Anonymous says:

    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

    • ssokolow says:

      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.

      • Neuruss says:

        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.

  2. Anonymous says:

    “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”

    • ssokolow says:

      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.

      • Anonymous says:

        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 🙂

        • ssokolow says:

          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.

  3. If you really do want the coercion-inducing ==, you can use “ to escape back into regular JavaScript.

    doSomething() if `a == “coerce”`

  4. Peter Lyons says:

     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

    • ssokolow says:

      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. 😛

  5. Jameson Quinn says:

     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,->

    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.

  6. Chris Alfeld says:

    For someFunction(x, y, *args) you can do

      someFunction( x, y, args… )

    • ssokolow says:

      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.

  7. ssokolow says:

    Good point. I’ve added a quick mention to the post with a link to your comment. 

  8. gasche says:

    > 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!

  9. gasche says:

    > 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!

  10. Zorg says:

    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)

    • ssokolow says:

       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.

      • Sadkkasldjaklsdj says:

        Actually, it means ((bar x) y).


        • ssokolow says:

          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.

          • Weeble says:

            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.

      • James says:

        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)))

  11. Pingback: Links for 2011-05-14 — Business Developer Talk

  12. Pingback: Peters Linkschleuder – Der Schockwellenreiter

  13. Jameson Quinn says:

    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.

  14. 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

    • ssokolow says:

      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.


  15. Anonymous says:

     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.

  16. Pingback: First Look at Python | Small Dropbear

  17. kracekumar says:

    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)

  18. Pingback: 8 PCs In A Bunker: Ownership, Respect, and Principles in the Steam Era | Stephan Sokolow’s Blog

  19. 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.

  20. Jo Liss says:

    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.

  21. 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!

  22. Take care here: is not is not isnt. not has a very low precedence in Python, only above and and or. 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 use unless here. Very unfortunate, in my view.

  23. 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 (maybe export?) 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 of window 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)

  24. RY says:

    About your question:
    “Concise any(iterable), all(iterable), and reduce(function, sequence)”

    I think this could be useful:

    [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 and Math.max.apply but, unfortunately, that reduce 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution under the same terms as the associated post.

All comments are moderated. If your comment is generic enough to apply to any post, it will be assumed to be spam. Borderline comments will have their URL field erased before being approved.