CoffeeScript for Python Programmers
CoffeeScript for Python Programmers
See https://edemaine.github.io/coffeescript-for-python/ for a better-formatted version of this document.
CoffeeScript is a programming language whose syntax is clearly designed to match much of Python (with additional inspirations from Perl, ECMAScript, Ruby, etc.). But most documentation for learning CoffeeScript assumes knowledge of JavaScript (which CoffeeScript compiles to), which is far messier.
This guide attempts to teach CoffeeScript to someone fluent in just Python (such as the typical MIT sophomore), showing the slight tweaks needed to convert Python code into CoffeeScript code. The goal is to make it easy for someone to learn CoffeeScript as a second language after Python, without first learning JavaScript, thereby enabling a Python programmer to also make cool web applications (for example). This should also make it easier to later learn JavaScript as a third language (as the CoffeeScript compiler and documentation provides countless examples relating the two).
This guide is still a work-in-progress, and is not yet complete. Feel free to submit an issue for anything you find lacking or confusing.
Why CoffeeScript instead of Python?
Both Python and CoffeeScript are great languages. The main reason to prefer CoffeeScript is that it compiles to JavaScript, resulting in several advantages:
- It can run in any web browser, making it easy to distribute your software for people to play with: just embed it in a web page, and anyone can run it on their desktop computer or smartphone. (This feature is also especially important for web development.) It can also run stand-alone/from a command line (like Python) via Node, which is now the basis for many web servers as part of a complete “web application”.
- When running in a browser, you gain access to many powerful GUI features in the browser, notably HTML/CSS (e.g., buttons), SVG (vector 2D graphics), Canvas (raster 2D graphics), and WebGL (3D graphics). This makes it really easy to do complex interactive graphics.
- It is much faster: Node runs typical CoffeeScript 2-5x faster than the equivalent Python (though this gap narrows if you use PyPy). This performance boost is thanks to extensive optimization, such as just-in-time compilation, because of the intense interest in web applications. Low-level access to typed arrays, WebWorker threads, etc. can make Coffee/JavaScript even faster. For example, this π calculation benchmark shows that well-tuned JavaScript code is around 5x faster than NumPy, 100x faster than CPython, and only around 2x slower than highly optimized C.
An alternative would be to learn a language like RapydScript which is more similar to Python but still compiles directly to JavaScript (like CoffeeScript does).
Major Similarities and Differences
Both Python and CoffeeScript share many features:
- Code block nesting is based on indentation.
- Variables don’t need to be declared; you just assign a value.
- Everything is an object and has a type, but you don’t need to declare the types of variables.
- Imperative programming plus strong functional programming support.
- Interpreted language with just-in-time optimization and interactive REPL
- Self-resizing arrays and dictionaries are powerful native types.
- Numbers,
strings,
regular expressions,
slicing,
comparisons with chaining,
and
/or
/not
,if
,while
,for...in
, list comprehensions, generator functions andyield
, asynchronous functions andawait
, exceptions, and many other features are all very similar.
They also have some major differences (some better for Python and some better for CoffeeScript):
- CoffeeScript requires less punctuation, and relies even more on indentation:
- no colons to start code blocks,
- optional braces around dictionary literals,
- optional quotes around dictionary keys,
- optional commas between list items, and
- optional parentheses in function calls.
- Variables have different scope: every variable in
CoffeeScript is as if it was declared
nonlocal
in Python. This makes it easier to access variables in enclosing scopes, but also easier to accidentally re-use variables. - The typing systems differ: CoffeeScript uses prototype object orientation, while Python uses multiple-inheritance object orientation. The main practical difference is that multiple inheritance is not supported by CoffeeScript. In principle, it’s also easier to create “classes” in CoffeeScript because every object can act as a class.
lambda
-style inline functions can be multiple lines in CoffeeScript, making mixed imperative/functional programming even easier. On the other hand, CoffeeScript functions do not support keyword arguments.- CoffeeScript’s REPL has a different interface for multiline inputs: press CTRL-V before the first newline, and press CTRL-V again when done (instead of a blank line).
- The built-in types differ in many small ways, e.g., their method names differ. But for the most part, there is a one-to-one mapping.
- CoffeeScript has more helpful syntax for a lot of important features,
but also misses a few features:
- There is just one
Number
type corresponding to Python’sfloat
. There are no (big) integers, complex numbers, or rationals. - String interpolation,
regular expressions, and
the equivalent of
range
have built-in syntax (instead of relying on methods/libraries/functions). - There are two slicing operators depending on whether you want to include the final index or not.
- All comparisons are shallow;
no built-in deep comparison support.
Truthiness is similarly shallow; e.g.,
[]
is considered true. unless
alternative toif not
;switch
alternative to longif
/then
/else
chains; andif
can come at the end of a line;- Multiline
if
s,while
, andfor
loops are expressions instead of statements, so single statements span multiple lines with full indentation support. (while
andfor
loops helpfully accumulate the list of final values.) - Three types
of
for
loops, including cleaner syntax for Python’sfor i, x in enumerate(...)
andfor key, value in d.items()
. - Exceptional behavior generally doesn’t raise exceptions
like it does in Python.
For example,
d[key]
returnsundefined
whenkey not in d
(instead of raisingKeyError
);1/0
returnsInfinity
(instead of raisingZeroDivisionError
); and function calls with incorrect number of arguments work fine (with missing arguments set toundefined
and extra arguments discarded, instead of raisingTypeError
). - No dictionary comprehensions or generator expressions.
- No operator overloading via
__special_methods__
. No metaclasses.
- There is just one
Given the large similarites, a natural question is whether it’s possible to automatically convert Python to CoffeeScript. We’ve started exploring that question in python2coffee. By contrast, this guide aims to teach you CoffeeScript so you can write code in CoffeeScript in the first place.
Quick Reference Guide
At a high level, if you remove all the colons at the end of lines from Python code, you end up with equivalent CoffeeScript code. There are many smaller differences, though, stemming in particular from different built-in types (a consequence of JavaScript). This section aims to document these differences by way of side-by-side examples.
print and assert
The analog of Python’s print
is CoffeeScript’s
console.log
.
Like Python 3, it is just a regular function.
Similarly, assert
’s analog is console.assert
(also a regular function, unlike Python).
Python | CoffeeScript |
---|---|
|
|
|
|
Comments
Python and CoffeeScript comments are generally the same:
Python | CoffeeScript |
---|---|
|
|
CoffeeScript also supports block comments (similar to triple-quoted strings in Python), which you should be careful not to trigger by accident:
Python | CoffeeScript |
---|---|
|
|
|
|
Strings
CoffeeScript string notion is very similar
syntax to Python, except in how triple-quoted strings deal with indentation.
In addition, strings enclosed with "..."
or """..."""
have built-in string
interpolation similar to Python 3.6’s
f-strings.
(If you want something like Python’s %
operator, try the
sprintf-js package.)
The resulting JavaScript String
type
has many similar methods to Python str
, though often with different names.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See also string slicing.
Numbers
JavaScript has just one
Number
type
corresponding to Python’s float
(IEEE double precision).
There are no built-in integers, big integers, complex numbers, or rationals.
(But BigInts are coming!)
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Functions
CoffeeScript functions
automatically return the last expression (if they are
not aborted with an explicit return
), making the final return
optional.
All arguments default to undefined
unless otherwise specified.
Defaults are evaluated during the function call (unlike Python which evalutes
them at function definition time).
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
In CoffeeScript (like Perl and Ruby), the parentheses in function calls
are allowed but optional;
when omitted, they implicitly extend to the end of the line.
(Similar behavior is possible in IPython via the
%autocall
magic.)
Parentheses are still required for zero-argument function calls and when
an argument list ends before the end of the line.
In CoffeeScript (unlike Python), there can be no space between the function
name and the parentheses.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
CoffeeScript functions do not support keyword arguments. The typical workaround is to use an object for an argument.
Python | CoffeeScript |
---|---|
|
|
|
|
CoffeeScript allows calling a function with a variable number of arguments,
and getting back all remaining arguments, with
splats (...
):
Python | CoffeeScript |
---|---|
|
|
|
|
Instead of ...x
, you can also write x...
. If you’re using CoffeeScript v1,
you need to use the latter form.
Variable scoping
CoffeeScript has no variable declarations like Python’s
global
and
nonlocal
.
CoffeeScript’s only behavior
is the equivalent of Python’s nonlocal
:
a variable is local to a function if it is assigned in that function
and it is not assigned in any higher scope.
An exception is that function arguments are always local to the function,
with together with CoffeeScript’s do
makes it simple to explicitly request
Python’s default behavior.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
if/then/else and switch
CoffeeScript if
s
are similar to Python’s, except that elif
is spelled else if
.
In addition, CoffeeScript offers unless
as a more intuitive if not
,
and allows a one-line suffixed if
(and unless
).
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Unlike Python, CoffeeScript offers a
switch
expression
as an alternative to if
/then
/else
.
switch
is especially concise when branching on the same value.
There’s also a one-line form for switch
cases, when...then
.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
while loops
CoffeeScript while
loops
are roughly identical to Python’s, including support for continue
and break
.
Like unless
, until
is shorthand for while not
.
In addition, loop
is shorthand for while true
.
Like if
and unless
, while
and until
have a one-line suffix form.
In addition, while
and until
loops are expressions, returning an array
of the final values.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
for loops
CoffeeScript for...in
loops are
similar to Python’s, including support for continue
and break
,
plus a concise notation for for...in enumerate(...)
.
In addition, for
loops are expressions
(like while
and until
loops),
returning an array of the final values.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
See also comprehensions.
Comprehensions
CoffeeScript array comprehensions
are similar to Python’s list comprehensions, but written with parentheses
instead of brackets and with when
instead of if
.
Unlike Python, they are just a one-line inverted form of a regular for
loop
(symmetric to the one-line inverted if
),
and can also be written in the non-inverted multiline form.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
CoffeeScript lacks dictionary/object comprehensions and generator expressions, though it does have generator functions, which can be used to simulate generator expressions:
Python | CoffeeScript |
---|---|
|
|
Comparison operators
Most Python comparison/Boolean operators have
the same name in CoffeeScript
(in addition to offering C-style names).
CoffeeScript also supports
chained comparisons
just like Python.
One key difference is that ==
and !=
are shallow comparisons, not deep:
they act like Python’s ==
/!=
only for numbers and strings,
and act like Python’s is
/isnt
for all other objects.
Another important difference is that []
and {}
are considered true
in CoffeeScript, so you need to check nonemptyness differently.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Python list / CoffeeScript Array
CoffeeScript arrays include
notation similar to Python list
s, as well as an indentation-based notion
that avoids the need for commas.
The resulting JavaScript Array
type
has many similar methods to Python list
, though often with different names.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CoffeeScript destructuring assignment is a nice tool for extracting parts of arrays:
Python | CoffeeScript |
---|---|
|
|
|
|
See also array slicing.
CoffeeScript has no analog to Python’s tuple
type, but the same effect of
“an unchangable list” can be obtained via Object.freeze
:
Python | CoffeeScript |
---|---|
|
|
Python dict / CoffeeScript Object
Python has two key/value mechanisms: hasattr
/getattr
/setattr
for object attributes and __contains__
/__getitem__
/__setitem__
for dictionaries. CoffeeScript has no such asymmetry, making regular
Object
s a fine substitute for dictionaries.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CoffeeScript destructuring assignment is a nice tool for extracting parts of objects:
Python | CoffeeScript |
---|---|
|
|
|
|
An important limitation of Object
s as key/value stores is that all keys
are mapped to strings: the reference d[x]
is equivalent to d[x.toString()]
.
One weird consequence is that d[42]
and d['42']
refer to the same item.
On the plus side, it gives you a limited way to override key equality testing
(like Python’s __hash__
and __eq__
), by using/overriding the
toString
method:
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
Python dict / CoffeeScript Map
While CoffeeScript Object
s are a good substitute for dictionaries,
they have
a few limitations,
most notably, that all keys in Object
s get mapped to strings.
An alternative is the built-in Map
type, which allows arbitrary object keys,
and distinguishes numbers (42
) from their string equivalents ('42'
).
Unlike Python’s dict
, however, Map
s offer no way to override the hash
function or equality testing (Python’s __hash__
and __eq__
): objects are
treated as identical keys
if they match according to
CoffeeScript’s ==
operator, so
numbers and strings are compared by value, while all other
objects are compared according to reference identity
(like Python’s is
).
(Also, unlike ==
, NaN
is treated equal to itself
and -0
and +0
are treated equal.)
Map
’s syntax is also uglier than regular Object
s.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Python set / CoffeeScript Set
CoffeeScript offers a Set
class similar to
CoffeeScript’s Map.
It shares the limitation of testing equality according to
CoffeeScript’s ==
operator, so
numbers and strings are compared by value, while all other
objects are compared according to reference identity
(like Python’s is
).
If you instead want “render as identical strings” semantics,
use regular CoffeeScript Objects
(with constant values, e.g., true
).
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Slicing and range
CoffeeScript slicing features two
notations for the range from i
to j
: i...j
excludes j
like
Python’s i:j
, while i..j
includes j
.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note that negative numbers behave like Python in slices,
but negative numbers behave differently when simply getting an item:
x[-1]
is equivalent to x['-1']
and will typically return undefined
;
to access the last element, use x[x.length-1]
.
Null values
Python has one “null” value, None
.
CoffeeScript has two, undefined
and null
.
Essentially, undefined
is the default initial value for all variables
(a notion absent from Python), while null
is an explicit null value.
Python | CoffeeScript |
---|---|
|
|
CoffeeScript defines an
existential ?
operator,
both a unary form to test for undefined
or null
, and a binary form
to provide alternate (default) values in case of undefined
or null
:
Python | CoffeeScript |
---|---|
|
|
|
|
CoffeeScript also defines
many conditional operators
that apply the operator only when the left-hand side isn’t
undefined
or null
(and otherwise leaves it alone):
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
Regular expressions
CoffeeScript has built-in Perl-like /.../
syntax for regular expressions,
and a triple-slash version ///...///
for multiline regular expressions
that ignores whitespace.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JavaScript regular expression syntax and usage is roughly the same as Python, with some exceptions:
- JavaScript doesn’t support
(?P<...>...)
,(?<=...)
,(?<!...)
,(?#...)
,(?(...)...|...)
,\A
, or\Z
in regular expressions. - In JavaScript,
/
needs to be escaped as\/
within/.../
. Also, spaces right next to the surrounding/
s can confuse the parser, so you may need to write them as[ ]
(for example). - JavaScript doesn’t support flag-changing syntax:
instead of
(?i)
and(?m)
, use/.../i
and/.../m
. - JavaScript doesn’t support flags
re.ASCII
,re.DEBUG
, orre.LOCALE
. (///...///
is the analog ofre.VERBOSE
.) - JavaScript’s
\d
matches just[0-9]
, and\w
matches just[a-zA-Z_]
, instead of the Unicode notions matched by Python. However,\s
matches all Unicode space characters like Python. - JavaScript replacement patterns need to use
$...
instead of\...
(and$50
instead of\g<50>
), and thus need to have$
escaped as$$
. The full-match replacer\g<0>
should instead be$&
. Additional replacement features are$`
, which expands to the portion of the string before the match, and$'
, which expands to the portion of the string after the match.
Classes
CoffeeScript classes behave similar
to Python classes, but are internally implemented with prototypes, so do
not support multiple inheritence. CoffeeScript provides @
as a helpful
alias for this
(the analog of self
in Python), and @foo
as an alias
for @.foo
.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
->
vs. =>
Within methods, use =>
in place of ->
to construct a function with
the same value of this
(@
) as the method creating it.
Python | CoffeeScript |
---|---|
|
|
Exceptions
Exceptions are far less important in CoffeeScript than Python because
most invalid operations return special values like NaN
or undefined
instead of raising an exception.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
But exceptions still exist, and can be captured in a similar way:
CoffeeScript try
is similar to Python’s,
except there is no exception type matching.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
See JavaScript’s built-in Error types.
Generator functions
CoffeeScript generator functions
are roughly identical to Python’s: any yield
statement makes a function
a generator function, and both support yield from
.
The main difference is in the resulting
Generator object,
which has a different next()
interface and does not support send()
.
Most important is that looping over generators (and iterators in general)
requires for...from
instead of for...in
in CoffeeScript.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
|
|
|
|
Asynchronous functions
CoffeeScript async functions
are similar to Python’s
(which coevolved to be similar to JavaScript’s),
except that there’s no need to declare a function async
; like
generator functions, any function with an await
keyword is automatically async
.
Python | CoffeeScript |
---|---|
|
|
|
|
|
|
Installation / Getting Started
To install CoffeeScript on your machine, first
install NodeJS.
(LTS = Long Term Support is a good choice.)
Or install NodeJS with a package manager.
This will install (at least) two commands, node
and npm
.
Then run the following command:
npm install --global coffeescript
You should then have a command coffee
that runs the interactive interpreter,
similar to python
. The main difference is, if you want to input a multiline
command, you need to press CTRL-V before the first newline, and press CTRL-V
again when you’re done (instead of indicating completion with a blank line).
You can also compile a CoffeeScript file filename.coffee
into a
JavaScript file filename.js
via
coffee -c filename.coffee
Modules (on Node)
Like Python, you can split up your code into multiple files, and add
dependencies between them.
In a Node environment, the analog of import
is require
:
Python | CoffeeScript |
---|---|
|
|
|
|
A key difference is that modules need to explicitly “export” variables
that they want to expose to other modules by attaching them to the
exports
object; local variables are private.
Python | CoffeeScript |
---|---|
|
|
Here is how to detect whether you are the “main” module (being executed
directly by coffee
or node
):
Python | CoffeeScript |
---|---|
|
|
Packages (on Node)
The analog of PyPI (Python Package Index)
is NPM (Node Package Manager).
The analog of command-line tool pip
is npm
(or an alternative called yarn
).
Unlike PyPI, NPM packages are usually installed locally to each project, which makes it easy for different projects to use different versions of the same package. To get started, run
npm init
This will ask questions for the creation of a stub package.json
file.
Then you can install packages local to your project using npm install
.
For example, to install the
underscore
package
(written by the same author as CoffeeScript), run
npm install underscore
This will install the package in node_packages/underscore
,
and change package.json
to note which version you depend on.
You can use a package installed in this way via
_ = require 'underscore'
It’s also easy to create your own packages and publish them for others to use.
About
This document is by Erik Demaine, with helpful input from several others. The top image is based on the CoffeeScript logo and this free Python clipart.