My Take on CoffeeScript
ruoyusun.com
ruoyusun.com
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) 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, failureThe 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.
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 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.
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?
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.
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.
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 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 : falseWithout syntax highlighting skimming over Coffee code is awful because you can't distinguish code from variables easy enough.
This is easier to read:
a && b && !c
Than this: a and b and not c
The form with symbols is easier to process because you can clearly distinguish its parts.What's cryptic about ?: ? You learn it just like you learn if ... then ... else and that's it, not cryptic anymore.
Not to mention the yes/true/on thing. What a waste of reserved words.
Text-like does not mean comprehensible. If that was true, then we'd write Coffee like this:
function sum with arguments left and right means
the result is left plus right
That's a lot less cryptic, right ?I also feel your semantic example is overly obtuse.
func SumOf left and right is left + right
--
result is SumOf left and right
--
Exactly what I think of if ... then ... else
IMHO, your plus sign is "pointlessly terse and cryptic". See what I did there? Now seriously, why func? I prefer Coffee's (arg1, arg2, ...) -> syntax for functions. What strikes me as odd is the fact that such a slim language gets verbose in such aspects.
Of course my example is overly obtuse: it's just an hyperbole!
But anyways, I suspect the if ... then ... else syntax is just a side-effect of ? being used as an existential operator in CoffeeScript.
No, I don't see what you did there because + has one and only one meaning, and that is add. Every child can identify what + means, the same cannot be said for (a,b) ->, even to developers who have developed their entire lives (unless they have experience with coffeescript). Hence, cryptic.
It's hilarious how you state some fact explicitly with your own words and yet completely fail to realize this fact.
Readability is not to be confused with familiarity - and you do exactly this. You say that if you learn something it becomes readable, which is not the point, and not true at all.
Think of readability as a measure of how many things you need to learn to understand something. If you introduce a symbol which meaning is completely arbitrary and needs to be learned, you make your code less readable. While Wikipedia page[1] concentrates on the readability of texts written in natural language, it suggests that the more words the text uses, the harder it is to read.
The second part of readability is text length, and so sometimes introducing a symbol to represent common concept, while making it harder to read by itself, makes the program more readable as a whole because it makes the program shorter. It's a trade-off that needs to be considered when designing a language.
The 'class' keyword in CS is a good example: it codifies a useful idiom, that in JS takes 5 lines of code, into a single word. While you need to learn it's meaning, which on the one hand makes the program less readable, on the other hand it reduces the length of certain class of programs significantly and so it makes them much more readable than JS counterparts.
The 'if ... then ... else' is a bad example. The ternary operator does not reduce the length of any program significantly, especially in the presence of the 'switch' statement. Every time you use a ternary operator you still need a test, true expression and false expression. Ternary operator introduces the infix syntax, and not one, but two symbols that you need to learn. It's bad, really bad for readability.
The code written like this:
if (alive and kicking or dead) then (something) else (something els)
is much more readable than equivalent: (alive&&kicking||dead) ? (something) : (something els)
because the latter is not significantly shorter and it introduces as many as 4 (!) symbols with arbitrary meaning. You may not recognize this, because you already know the meaning of all of these, but objectively the latter form is less readable.Your statement that boolean operators are more readable as symbols than as words is simply not true, at least if we use the definition of readability from wikipedia. You are much more comfortable with these operators as symbols, you're more familiar with them and used to them, but that is something entirely different than readability. You think that it's readable, while it's not.
I wish more people actually knew what readability is. Relevant info is just a click away, yet everyone seems to have his own definition of readability, which makes any discussion highly subjective and useless in the long term.
[EDIT] Forgot to mention, this is exactly why I find discussions on Lisp or Smalltalk syntax so infuriating. People are not interested how readable the syntax actually is, that is - how many symbols it introduces and how many of them are reused in different places. They say that the new syntax is "unreadable", while it is much more readable than alternatives, just unfamiliar.
Finally, drop the idea that your view is "objective" and there aren't different opinions on readability.
EDIT: what I mean is that discussing readability is pointless if all the participants have different definitions. I agree that the wikipedia page I linked to does not provide the best possible definition because it talks about texts in natural languages and we're talking about source code. Still, using not-the-best definition is better than using many different definitions.
And I don't disagree with you. Coffee is less readable than JavaScript: "->" is less readable than "function", for example, and "@" is less readable than "this". It's a matter of trade-offs, these shortcuts make the language harder to read, but easier to write; the assumption here is that people will be able to deal with this level of brevity when reading.
I went and googled for a bit for articles talking about code readability, I found a few interesting discussions:
http://queue.acm.org/detail.cfm?id=957782
http://www.perlmonks.org/?node_id=592616
http://www.joelonsoftware.com/articles/Wrong.html
Worth reading.
It says what it does then, doesn't it?
He is just demonstrating how `if` is also an expression, the comparison and return could be anything.
There are better examples of "everything is an expression", like "for" loops and multi line "if then" blocks.
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 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).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.
I bet someone has uttered nearly these exact words when debating assembly versus C/Fortran/etc.
The problem, in other words, is not about CoffeeScript in particular. It's about people learning CoffeeScript and not learning JavaScript. We are far, far from the point where you can be a great web developer without knowing JavaScript.
> 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?
This seems to imply to me you think most coffeescripters don't know javascript?
I've always thought coffeescript was for people who _did_ know javascript, and just wanted a little neater syntax.
Why use it? For the same reason I tweak my text editor: when I spend hours a day doing something, even small efficiency wins add up.
I agree that learning only coffeescript, and not javascript is a bad idea.
edit: toned down language.
This may be a temporary problem, though, as it may also become increasingly unnecessary to know JavaScript. But it WILL be a problem.
This isn't and won't be limited to CoffeeScript though. There are still a ton of "developers" out there who know jQuery or Dojo or, God forbid, Ext.js but actually know little JavaScript. A friend of mine was recently interviewing a guy who claimed to be a JavaScript developer but didn't know that "foo['bar']" was the same as "foo.bar". No doubt the guy could glue things together in some library blindfolded but the fact remains; he didn't know JavaScript and it cost him a job.
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)) function(data) { return doSomething(data); }
With doSomething
Named functions are just as first-class as anyonymous ones...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. f = obj.doSomething.bind(obj);
Wrapping functions like in the example is different and is very common among cargo cultistsWell, 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.
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.