

My Take on CoffeeScript - insraq
http://ruoyusun.com/2013/03/17/my-take-on-coffeescript.html

======
arnarbi
> set(User, function() { return {name: "John"}; }, silent: true);

That's not even valid JavaScript, so I would not expect that. It is also
somewhat standard that lambda-like operators eat as many tokens as possible.

But I agree with many points. The anonymous function sugar is extremely handy
in callback-oriented programming. I wonder if a small syntax extension to JS,
consisting of Python like indetation and Ruby like blocks would ease the
biggest pains:

    
    
        function f(some_param, callback)
          bla()
          return 1
    
        f(10) |u,t| ->
          callback_code_here
    

The call to f would receive the anonymous callback as its last argument.
Callbacks in other positions, or multiple callbacks could be named:

    
    
        f(10, success, failure)
          succss: |data| ->
            console.log("Success!", data)
          failure: |error| ->
             console.log("Error: ", error)

~~~
mratzloff
This

    
    
        function f(some_param, callback)
          bla()
          return 1
    
        f(10) |u,t| ->
          callback_code_here
    

or

    
    
        f(10, success, failure)
          succss: |data| ->
            console.log("Success!", data)
          failure: |error| ->
             console.log("Error: ", error)
    

are not simpler than valid CoffeeScript, such as

    
    
        f = (someParam, callback) ->
          bla()
          callback 1  # presumably your intent
        
        f 10, (u, t) ->  # callback code here
    

or

    
    
        success = (data) -> console.log("Success!", data)
        failure = (data) -> console.log("Error: ", error)
        f 10, success, failure

~~~
arnarbi
I agree, and I don't want to simplify CS, but rather JS. The thing that
bothers me about CoffeeScript is the call syntax without parenthesis but with
commas - which seems to me the main source of "surprises".

The main benefit of it however is allowing you to write anonymous function
arguments without chasing closing delimiters.

I would like something that lies in between JS and CS, with that particular
benefit but without the weird grammar.

~~~
mratzloff
My solution above (naming functions) is one solution to the problem.

Wrapping ambiguous objects or arrays is another:

    
    
        f 1, 2, {
          foo: "bla"
          bar: "bla"
        }, (error, result) ->
          # do something with result
    

For a series of function parameters, the leading comma is another:

    
    
        f.asyncMap (item) ->
          foo item
        , (error, result) ->
          # do something with result

~~~
arnarbi

        f.asyncMap (item) ->
          foo item
        , (error, result) ->
          # do something with result
    

The readability of that is terrible. I (and most people) can easily figure out
how to make myself understood to the parser, that's not an issue at all.

The itch that CS scratches is severe, but I believe there is an opportunity to
have something much better in terms of readability and predictability.

~~~
mratzloff
Which is why it was the third alternative, and not the first. :-)

------
mistercow
While I love CoffeeScript, the always-implicit-return thing is my biggest pet
peeve. The language really needs a non-implicit-returning arrow like -/> , if
nothing else so that you can make one liners that don't return a value.

Unfortunately, this isn't the direction they want to take the project in. The
reasoning is that in CS, all functions should ideally have a return value. I
think that's silly, since you have to contend with the fact that it's supposed
to interoperate with JS, and plenty of builtins and JS libraries _don't_
behave as if every function has a return value, but what can you do?

~~~
masklinn
> it's supposed to interoperate with JS

Definitely the crux of the issue, as languages/ecosystems where "callables
always return a value" tend not to have that issue even when dynamically typed
(Ruby, Erlang). And it's not just JS interop, but _gateless_ interop: calling
JS from CS or CS from JS is supposed to be "invisible" as opposed to e.g.
ClojureScript which also has excellent JS interop but where the interop is
done through special forms (e.g. the `js/` namespace calls into JS-global
objects), managing developer expectations about the seamlessness of the
boundary.

~~~
mistercow
Exactly, and the article's example is spot on for how these expectations get
violated by CS's behavior. In CS, the assumption is that aside from minor (and
easy to detect) performance penalties, returning a nonsensical value from a
function is harmless.

But libraries like jQuery actually give _meaning_ to "no return" in some
callbacks. They cleverly let that be the most common behavior, and then let
return values specify less common behavior. An example of this in jQuery that
creates even more aggravating bugs is event handling. Here's a simple example:
<http://jsfiddle.net/63fUY/> .

The expectation is that each time you click either button, the total count
will increase. But because of the implicit return on line 13, preventDefault
gets called half the time. That's an incredibly easy mistake to make, a huge
pain to hunt down, and awkward to write a test for. And it would be so easy to
avoid if the language had a syntax for "default to no return" which we could
just use on all event handlers.

~~~
masklinn
> Here's a simple example: <http://jsfiddle.net/63fUY/> .

Hah. Fun one.

> And it would be so easy to avoid if the language had a syntax for "default
> to no return" which we could just use on all event handlers.

Alternatively, rollback the "implicit return" but make the explicit return
short and sweet yet still visible. E.g. use Smalltalk's unary `^` (which as
far as I know currently exists neither in JS nor in CS) so the callbacks would
be:

    
    
        ->
            totalClicks++
            $('#total').text totalClicks
            ^false # explicitly return false
    

and

    
    
        ->
            $('#state').text if nextState then "On" else "Off"
            nextState = !nextState
            # no explicit return -> undefined as in JS

------
FuzzyDunlop

        isLargerThanSix = if x > 6 then yes else no
    

You don't need CoffeeScript to do something as redundant as that, since
comparison operators return a boolean:

    
    
        var isLargerThanSix = x > 6
    

Of course, CoffeeScript doesn't even offer an improvement for the level of
explicitness in the example, since it removed the ternary operator. This
javascript is invalid CoffeeScript:

    
    
        var isLargerThanSix = x > 6 ? true : false

~~~
ricardobeat
Ha. When I read that example I thought "poor guy, he should have chosen a
better one, now this will be nitpicked to death".

He is just demonstrating how `if` is also an expression, the comparison and
return could be anything.

~~~
insraq
You are right. I should. This is definitely not a good example of showing
"Everything is an expression" is CoffeeScript.

------
niggler

         I certainly agree that CoffeeScript has a bad predictability - it is full of surprises. 
    

This 100% captures my experience, especially when it comes to interstitial
whitespace sensitivity

~~~
icambron
Me too. I love CoffeeScript, but even after writing tons of it, I occasionally
get frustrated by some whitespace/precedence issue.

~~~
niggler
I wonder if there's a good IDE or linter to pick up on these issues while you
code. It would be difficult to farm out to the compiler on every character
change, but I suspect something like that is needed

------
stdbrouw
I don't really understand this "bad predictability" thing. If you leave out
parens, CoffeeScript always wraps to the end of the line, simple as that. Hard
to visually parse at first sight in certain scenarios? Use parens, that's what
they're there for. Problem solved. I'd absolutely hate it if CoffeeScript was
inconsistent in how it handled these things, but it really is not.

~~~
kaoD
Not to the EOL.

    
    
      a = b c d,
        e: 1
        f: 2
    

Translates to:

    
    
      var a;
    
      a = b(c(d, {
        e: 1,
        f: 2
      }));
    

Though that's still predictable for me, I don't have problems with
parenthesizing in Coffee (I did the translation off the top of my head and I'm
pretty sure it's correct).

------
harel
CoffeeScript and co. ensure new developers who adopt those languages never
actually be proficient in JavaScript. Its not a good thing. Perhaps its me,
but I just don't get why I'd want to write in one language in order to get
code in another. Why not just learn the target language? And please don't
bring up the "You write C which gets compiled to assembly or Python which ends
up as C" etc. Its not the same thing. JavaScript is not Assembly and its not
Machine Language. Its a very simple language and its quirks can be learned
quite easily. Its not that more verbose than CoffeeScript. Don't get me wrong,
I think CoffeeScript's syntax is nice, but its nice in an academic kind of
way, not in as a replacement for a language that if you work and develop for
the browser, you should know very well and be friends with. Its a dangerous
short cut.

~~~
account_taken
One cannot be proficient in CoffeeScript without being proficient in
JavaScript. How could they debug anything?

~~~
hilko
Exactly. I would never advise someone to use CoffeeScript without first
knowing JavaScript, but I think very few people would do this anyways.

And even if they tried, aside from debugging issues, I think the article shows
that it's pretty hard to work with CoffeeScript if you don't understand the
underlying output code.

------
account_taken
CoffeeScript is more readable if you follow Visual Basic paren rules. Visual
Basic, at least the one I used way back when, differentiates between functions
and subroutines. Functions whose return values are used in an expression must
use parentheses. Subroutines and functions used as subroutines omit
parentheses.

Example, I often see this. In this trivial example the intent is simple. In
real code this style is unnecessary cleverness.

    
    
        a b c, d e
    

In VB, since the return value of `b` and `d` are used, parentheses must be
added

    
    
        a b(c, d(e))

------
esailija
You can replace

    
    
        function(data) { return doSomething(data); } 
    

With

    
    
        doSomething
    

Named functions are just as first-class as anyonymous ones...

~~~
mistercow
While in most cases you can do that, they are not exactly equivalent. For
example, you might be passing the callback to a function that attaches members
to it.

But usually you see wrapping done in order to bind methods, like

    
    
      f = function(data) { return obj.doSomething(data);}
    

If you replaced that with

    
    
      f = obj.doSomething;
    

you'd likely have broken code.

~~~
esailija
A native method for binding methods has existed for a long time and is called
just that:

    
    
        f = obj.doSomething.bind(obj);
        

Wrapping _functions_ like in the example is different and is very common among
cargo cultists

~~~
mistercow
>A native method for binding methods has existed for a long time and is called
just that:

Well, bind is a relatively recent addition. It wasn't added to IE until IE9,
and it wasn't added to Safari until March 2012. In any case, I think that

    
    
      f = (data) -> obj.doSomething data
    

is far more readable than

    
    
      f = obj.doSomething.bind obj
    

Edit: it's also worth mentioning that in Chrome and FireFox, at least, the
latter version of f is an order of magnitude slower than the former
(<http://jsperf.com/native-vs-non-native-bind>)

But yes, wrapping functions is usually (but not always) silly.

------
crazygringo
For what it's worth, this post nearly _exactly_ mirrors my personal
experiences with CoffeeScript. It's a great writeup.

~~~
michaelwww
I'm glad to see you agree. I haven't had time to evaluate CoffeeScript, and
now I don't need to make it because the advantages CoffeScript offers are not
issues that bother me and it introduces issues that would bother me.

~~~
betterimposter
Yea, but the write-up failed to mention a few of the other advantages, most
notably, comprehensions. Every time I do a for, map, filter, etc. in js, I
wish I was writing coffee. Having said that, if the verbosity of array ops
aren't a sticking point, then I say don't bother. I'd probably be just fine
with js if I'd never typed

dog.praise() for dog in dogs when dog.type is 'pet'

I have gotten into the habit of singing these, so its probably just an oddity
I have. Carry on and ignore.

~~~
michaelwww
To each his own. I quite happy with C-like languages with braces for blocks.

------
PasserBy2
"...since most people are brainwashed by class-based mechanism" - that is a
very constructive language indeed.

