Skip to content

Instantly share code, notes, and snippets.

@AsaAyers
Created September 20, 2016 15:52
Show Gist options
  • Select an option

  • Save AsaAyers/d09e4de118b8d6b5e2d8fa3e38e496e0 to your computer and use it in GitHub Desktop.

Select an option

Save AsaAyers/d09e4de118b8d6b5e2d8fa3e38e496e0 to your computer and use it in GitHub Desktop.

Revisions

  1. AsaAyers created this gist Sep 20, 2016.
    156 changes: 156 additions & 0 deletions you_dont_need_coffeescript.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,156 @@
    # You Don't Need CoffeeScript

    It's time to replace CoffeeScript with [Babel](http://babeljs.io/) and
    CoffeeLint with [eslint](http://eslint.org/).

    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][transpilers] 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](https://github.com/AsaAyers/coffeescope), 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](https://github.com/michaelficarra/CoffeeScriptRedux/commits/master). That
    project appears to be dead.

    While CoffeeScript gained the ability to produce generators, they [can't
    implement syntax to use
    them](https://github.com/jashkenas/coffeescript/issues/3832) 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](http://blog.heapanalytics.com/goodbye-coffeescript-hello-typescript/)


    ## Significant Whitespace

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

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

    When you chain calls together CoffeeScript silently consumes whitespace.

    ```CoffeeScript
    $('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.

    ```CoffeeScript
    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][map] and
    [Array.prototype.filter][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



    [map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
    [filter]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
    [transpilers]: https://youtu.be/DspYurD75Ns?t=11m56s