Skip to content

Instantly share code, notes, and snippets.

@AsaAyers
Created September 20, 2016 15:52
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AsaAyers/d09e4de118b8d6b5e2d8fa3e38e496e0 to your computer and use it in GitHub Desktop.
Save AsaAyers/d09e4de118b8d6b5e2d8fa3e38e496e0 to your computer and use it in GitHub Desktop.
You Don't Need CoffeeScript

You Don't Need CoffeeScript

It's time to replace CoffeeScript with Babel and CoffeeLint with eslint.

CoffeeScript was useful a few years ago. It provided many features that JavaScript was lacking. It gave us the fat arrow (lexical this functions), default parameters, destructuring assignments, splats (spread operator), a class keyword, block strings, and more. Everything in the list above is now part of the JavaScript standard. JavaScript is moving forward and gaining adoption and better tools where CoffeeScript isn't.

But why isn't CoffeeScript getting better tools? Let's take a look at what Jeremy Ashkenas had to say in CODE GENIUS - Rise of the Transpiler at 11:56.

We like to cheat and make things easier for ourselves ... this is not kosher, it is introducing context sensitivity into the lexer process. If you took the actual class you would get an F doing this kind of thing. But it's just programming, it's just code. You can cheat if you want to cheat

In the end that kind of cheat meant that I can't build a tool that tracks variables, so I can't reliably build lint rules around unused or undefined variables. Typical compilers produce an Abstract Syntax Tree, or AST. The AST is a data structure that has all the info needed to produce target code. But, CoffeeScript's "AST" isn't a data structure to be read, instead it's a mix of data and executable code that modifies its own structure as it runs to fill in things like implicit returns.

Surely CoffeeScript Redux will save us! The description at the top of the project is "rewrite of the CoffeeScript compiler with proper compiler design principles and a focus on robustness and extensibility". The last change in that project that actually touched code was over a year ago. That project appears to be dead.

While CoffeeScript gained the ability to produce generators, they can't implement syntax to use them because for...of already has an incompatible meaning in CoffeeScript.

Surely there must be some benefits of using CoffeeScript today. If you look through coffeesript.org's home page, these are the features, or sometimes mis-features that CoffeeScript provides.

Optional function parenthesis

pop quiz! Given this line of CoffeeScript foo bar and hello world which of these is the JavaScript it produces

  1. foo(bar) && hello(world)
  2. `foo(bar && hello(world))``

I got that example from Goodbye CoffeeScript, Hello TypeScript

Significant Whitespace

The compiler is so loose that it only cares that blocks are indented different from the parent.

if zeroSpaces
 if oneSpace
   if threeSpaces
       if sevenSpaces
           undefined # 11 spaces

When you chain calls together CoffeeScript silently consumes whitespace.

$('body')
    .addClass('k')
    .removeClass 'k'
    .animate()
    .hide()

You might think those calls are indented, but technically they aren't. CoffeeScript simply consumes that indentation forcing CoffeeLint to need special hacks to lint that code.

Lexical Scoping and Variable Safety

"Safety" is a hilarious claim. Your variables are actually far less safe because they are implicitly declared. how many times have you had a path = require('path') in your code, and then somewhere you have a loop that operates on a file path, so you called the variable path? I'd have a rule catch this for you, but as explained above, you can't build one properly out of the AST. There is a 3rd party rule that attempts this, but it's incomplete.

scope1 = ->
  # bar is created here in scope 1 thanks to variable hoisting

  scope2 = ->
    # foo is created here in scope 2
    foo = "foo"

    scope3 = ->
      console.log(bar)


  bar = foo # outermost foo reference. Doesn't create the variable
  # outermost bar assignment does create the variable

Everything is an Expression (implicit returns)

Everything being an expression did seem nice when I used it. Implicit returns for any multi-line function is just a bad idea though. You can never really tell if a function needs to return something or not, because it will either way. I have had times where I had to go track down all the callers of a function to figure out if any of them were using the return value or the return value was accidental because I didn't opt out of it with an explicit return undefined

Literate CoffeeScript

It's an interesting idea, but I never found it useful. I tried to use it a few times, but it didn't really make sense in any project I ever worked on.

Comprehensions

These seem nice, but are easily reproducible with Array.prototype.map and Array.prototype.filter.

Array Slicing and Splicing with Ranges

These are nice.

Operator Aliases

You get to write and instead of &&! I'm not really impressed. This is kind of nice sometimes, but it's not significant.

Existential Operator

I want this in JavaScript.

Chained Comparisons

healthy = 200 > cholesterol > 60

This would be nice, but since we don't have it you just repeat the inner variable.

const healthy = 200 > cholesterol && cholesterol > 60

Block Regular Expressions

See https://www.npmjs.com/package/xregexp

@pts-sergiomartins
Copy link

Thank you for writing this!

@ahmader
Copy link

ahmader commented Feb 11, 2018

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment