CoffeeScript: less typing, bad readability
ceronman.com
ceronman.com
If you're looking for more regular style, this style guide is a better place to start:
Note: I'm not saying Crockford was singularly responsible for this, but The Good Parts was certainly well-received and very much popularised for it's opinionated views on the language.
Edit: I mean in terms of coding style, CS itself has already cut out a lot of The Bad Parts of JS
And the worst part of it is, nearly all the confusing/ambiguous/different compilation examples given rely on the undocumented rules CoffeeScript uses for parsing. It's amazing that, still, CoffeeScript's entire documentation is basically a "getting started" guide, and there is simply no reference documentation.
- Implicit is better than explicit
- There is more than one way to do it
These are the exact _opposite_ of a couple of the principles of the design of Python[1].Compare with Go, where you can easily read the whole spec and keep the whole language in your head: http://golang.org/ref/spec
The "The Smalltalk Language" section of the last ANSI Smalltalk draft is a more reasonable 28 pages.
I won't say it's bad, as it is nice to work with when you're writing it, and I do quite like it. But I could never recommend it to someone who didn't have a good knowledge of javascript already.
To that end, the docs on Coffeescript.org are the reference documentation (as they are the only official documents to reference) and they've suited me just fine. I get the sense that you're looking for a document in the style of ECMA-262, rather than one in the style of MDN.
describe 'Calculator', ->
it 'can add two positive numbers', ->
result = calculator.add 2, 3
assert result is 5
it 'can handle negative number addition', ->
result = calculator.add -10, 5
assert result is -5
Mandatory parentheses make significant whitespace lose much of its appeal when passing a block construct into a function. This is a large part of the reason why Python limits lambdas to a single line...Python doesn't limit lambdas to a single line, but to a single expression. That is a rather different constraint.
I think CoffeeScript has great readability, because I’ve taken the time to learn how it works.
If you have to take the time to learn how to read something, it's not easily readable.
CoffeeScript is only hard to read if you try to read it with a JavaScript mindset.
Yes, why would anyone approach a tool pitched explicitly at JavaScript developers, whose "golden rule" is "it's just JavaScript" (see the top of http://coffeescript.org/), with a "JavaScript mindset"? Truly, it is a mystery.
I don't think that's completely true. If you're used to algol or C-style, Lisps tend to be hard to read. Concatenative languages even more so. Not necessarily because they're hard to read in and of themselves, but because the "shape" of the code is "all wrong" compared what you're used to, and your usual anchors (keywords, binary operators, infix assingment, ...) are nowhere to be found.
I mean Arabic or Korean are not necessarily harder to read than English, but you've got to know them and how they work before you even recognize patterns in the squiggly lines and little squares.
They author's point is that Coffeescript is hard to read because it has too many edge cases which are not easily understood under a smalla and consistent set of rules. I have not programmed with CS, but it sounds like a convincing argument.
The point TFA raises is that people who're familiar with CoffeeScript syntax may still make mistakes resulting in completely erroneous but valid code. Yikes. And the inherent nature of whitespace or indentation makes the problem harder to detect by cursory inspection.
I have no strong opinions about CoffeeScript, myself. I just thought this was an interesting distinction, and a rare case where syntax makes a significant difference.
This isn't specific to Lisp, either. I posit that if you took your average C-like and removed indentation from any reasonably sized function or method (one or more loops, one or more if/else), you would have a hard time counting brackets.
i recall the days when i was thrown into a project where the requirement was to use mootools, rather than jquery. even though they are both "javascript" in nature, it severely limited my productivity. i can see the same thing happening to companies trying out coffeescript, it may seem like you are doing the better thing long-term wise, but in reality, it is not so much. it's better to just stick to standards and you'll be more productive down the line, i can almost guarantee it.
Maybe saying it's widely accepted isn't entirely appropriate, but it certainly is gaining a lot of traction.
Once you are releasing things outside of your controlled/team environment using anything but JS is just a loop for others to jump through.
Considering that, releases would be in JS, as they should be.
So you had jQuery experience and were inhibited by working on a MooTools project. I get that, makes total sense.
But then you say that switching to CoffeeScript may seem like a better long term choice but "in reality it is not so much."
Surely MooTools would've been fine for you as a long term choice.
Yes, there is a penalty to switching. Whether it's jQuery to MooTools, JS to CS, whatever. But that doesn't make it a bad long term choice.
Also, CoffeeScript is more than just saving keystrokes. That makes it seem like it's all about initial up-front cost savings. Instead, it's about having shorter, more expressive code. That pays dividends for the life of a project.
yes, but at the expense of a lot of other negative baggages that comes along with it. by having it's own compiler rather than leaving that to the browser seems like it's opening a can of worms.
and just reading from the comments, i can see how we could soon see a birth of "another language", that's supposed to be solve all problems coffeescript'ers face.
for one, i guess readability seems to be the common problem, which isn't good for productivity.
That's called incremental progress a d most people see it as a good thing. Others yell, "Get off my lawn!"
Don't you find it a little odd that you have such strong opinions on something you've never even tried?
if you now switch to coffeescript, something not remotely close to javascript or any other languages fwiw, you'll experience un-productivity. it's akin to having your top developers walking in eggshells and doing nearly nothing close to what he is capable of.
i think coffeescript is great in the sense that we are trying to create a better language. but i also sense how much damage it can bring to budding startups who can't find talented coffeescript'ers to fill the void when their lead developer moves on to other things.
a one off go project, i'd say it's fine. but anything more complex, i think you'll see programmers wishing they had gone back to what they were familiar with.
That's true for any tool/language/platform/whatever, everything has a learning curve (and coffeescript's is quite short). Going by your words we shouldn't ever learn anything.
And you shouldn't switch to a language if your developers are not comfortable with it. Do some experiments, try it out on a smaller project, then you might switch if you see good results.
What? What language can you read without knowing it first?
The same is true for programming languages. Sure, anybody can read Python. But it's fairly verbose and attempts to mirror English. On the other hand, Haskell is somewhat terse and attempts to mirror mathematical notation. I've found that, after about the same amount of time with either language (about a year of off-and-on use for both), Haskell becomes far easier to read than Python.
So just because something is easier immediately does not mean it is any more readable. It could, in fact, be less readable and merely more familiar!
Other examples that highlight how understanding of mathematical research involves much more than just reading: https://docs.google.com/viewer?a=v&q=cache:5NM3XjxAWfUJ:... http://math.stackexchange.com/questions/13460/how-to-read-a-...
Experts don't take time to read notations. They take time to understand the full scope of consequences embedded in a line composed of such a condensed and readable notation, within the frame of an otherwise complex proof.
> Mathematical notation is not readable, no.
I beg to differ. You mention the recent possible proof of the ABC conjecture, and I happened to browse through various materials on the subject. While I am far from being able to understand the crux of the proof, I can perfectly read the various components of it. In fact the notation system is so readable it actually helps tremendously in grasping concepts previously unknown to me.
It's also as bad as natural language for ambiguity and informality.
I don't think either English text or mathematical notation is a magic wand for readable programming language; It's more about reducing accidental ambiguity and the amount of mental context needed for parsing 'natural' constructs in the language.
Given that CoffeeScript doesn’t fix any of the
fundamental problems of JavaScript
same for a comment here: coffeescript doesn't bring much to table
There is so much coffeescript adds that I find these laughable: string interpolation
multi-line strings
comprehensions
guarded loops (hasOwn...)
easy constructor/prototype definition (class)
avoiding global leaks/shadowing
function binding
splats, defaults
safe usage of reserved names
existential operator
chained comparisons
READABLE regular expressions
This just off the top of my head. These make a huge difference to writing code everyday. I've been writing CoffeeScript for 90% of my projects in the past 2.5 years and it pretty clearly affected my productivity for the better. I can only conclude that whoever says "coffeescript doesn't add anything" hasn't really used it for work.Case in point, if you look at the linked article at the end you'll see an update by the author: I actually love CoffeeScript now that I've been writing it for a year.
The problem that he points out about "Implicit is better than explicit" and "There is more than one way to do it" are quite serious problems that modern languages shouldn't have.
However a lot of criticism seems to come from the fact that the author is a Python programmer and therefor wants CoffeeScript to be more like Python. No, I am not saying something like the author not understanding it isn't meant to be Python, but for example the implicit vs explicit debate is certainly a more philosophical view. Ask people who prefer other programming languages and they will have a different view. Hey, after all that's why there are so many, even if Ruby, Perl, Python, Falcon, etc. have very similar use cases.
My opinion for example is that dynamic languages are very implicit in first place so saying you don't want that (at all) doesn't make too much sense. I also don't have problems reading CoffeeScript, but occasionally had problems with very explicit languages being too verbose which can make it harder to follow. So looks like my mind works slightly different here.
But back to the article. There are lots of valid points. I think coding guidelines, which one should have working in a team, no matter what language could solve some of them. Some points look a bit like mixing different styles on purpose and at least can't see how you could find something like that in the wild. Just because you can code ugly, it doesn't mean you have to, but again that is more of an opinion. Some people like usually verbose languages, because they say they are easier to read, others like ones with shortcuts or where you have multiple options to express things, making it easier to read (for some people). I for example always enjoyed the fact that Perl has unless and until in place of negated if/while.
But that's more what you prefer. But hey, CoffeeScript is all Javascript, so if your team doesn't like it it's (comparatively) easy to step by step switch back.
Many of the issues raised in this article can be solved by "simply" not doing it (admittedly not always a real solution). Just because the language allows something to be done, doesn't mean it should be done that way. Our team has a styleguide that clearly explains good and bad practice, for CoffeeScript AND Python. (It's possible to do bonkers stuff in Python, too, just harder.) It includes things like: use explicit returns, especially when intending to return nothing; include parenthesis unless it's more clear without them (callback as arguments).
Yes, it'd be great if the language were more explicit and made it harder to do confusing thing. But, its core goal is being "just javascript", which prevents some of that explicitness. Also, the flexibility lets the real goal be clarity.
I find
my_object =
key: 'value'
fn: (response) ->
console.log(response)
to be more clear than my_object = {
key: 'value',
fn: function() {
console.log(response)
}
}
because it doesn't have all the crap. CoffeeScript's whitespace syntax is even more helpful when those objects start getting nested. Plus, not having to deal with trailing commas is amazing. At the same time, someFn arg1, arg2, ->
doStuffInACallback()
, arg3
can be confusing, so parens help: someFn(arg1, arg2, ->
doStuffInACallback()
, arg3)
I try to not be "that guy" when it comes to CoffeeScript, but it's easily one of my favorite languages now. All the crap that JavaScript requires is just gone.I think a majority can grok and parse Python, but the same can certainly not be said for CS. It's unfortunate too, because it has some Good Parts.
Readability is very subjective and depends on the user's knowledge of the language, as well as personal style, or 'accent', if you will. Code written with, for example, leading commas in dictionaries instead of trailing commas just looks bizarre to me and is a little harder to read, to me, even though I like to do something similar and stack colons.
someFn(arg1, arg2, (-> doStuffInACallback()), arg3)
But far better, if you have any say in it, is making the callback the final argument to someFn.
someFn arg1, arg2, arg3, -> doStuffInACallback()
The thing about coffeescript is that most of the pitfalls being discussed here are very easily avoided with a modicum of experience and sense, whereas in javascript, many of the core issues simply cannot be avoided, and must be worked around in plainly ugly and bug prone ways (see: string concatenation, this/that juggling, "])})]));, just for starters).
do_something(with, these, args) if im_supposed_to
I mean, the processor/interpreter always needs to evaluate the "if" first, so what purpose does it ever serve to put it after the "then"? To me, it just confuses things because it feels like code is getting executed backwards -- like crossing an intersection, and then checking to see if the light is green.I know it works "in English" ("do this if that"), but when I scan other people's code I'll sometimes completely miss the "if" (sometimes it's just off the screen).
Are there any examples where this reverse-if actually helps, instead of harming, code intelligibility?
throw 'error' if badness
return null unless goodness
Since it doesn't make sense to return or throw unconditionally it is makes it pretty clear that you should look for the if/unless. if badness throw 'error'
unless goodness return null
to be more readableAnd anytime there's code after a return, it had better be connected to a conditional. :)
if badness
throw 'error'
unless goodness
return null value = cache[key]
if value?
return value
return file.readContents() unless not file.exists() myfn = (arg) ->
return 1 if arg is "one"
return 2 if arg is "two"
return arg.toInt()
This is obviously a little contrived with such a short method, but helps when you have stuff to do between conditional returns.When scanning and I see
If (condition) { perform_some_action(foo, bar, baz); }
And I know condition is false I can ignore what follows but perform_some_action foo, bar, baz if condition
I now have to stop and parse the entire line. But not only that, because it can appear at the end I need to parse every line to make sure I don't get caught by some if or unless. Short lines are easy sure but it's still annoying to me. if (some condition that doesn't hold on that configuration) ...
You can avoid everything inside that if statement if you know that's not important for the case you're considering. read(file)
write(file) unless terriblyUnlikelyThingHappens
return results
... it helps to keep the expected main-line flow of your instructions reading smoothly -- much the same as the way you'd tend to use it in English. Doing it the "normal" way instead: read(file)
if (!terriblyUnlikelyThingHappens) {
write(file)
}
return results
... makes it a bit more difficult to follow what should be going on in the function.But that said, the last couple of projects I've written in pure JavaScript and it's actually made things much easier to go back and read. Like the author of this post, I too have had difficulty reading back over my old code and, at a glance, understanding precisely what's going on.
Over the past couple of months I've gone from being absolutely devoted to CoffeeScript to on the fence, mostly due to readability.
(I'd love to see a fork of CoffeeScript that forces syntax.)
A compiler option that enforces syntax would be nice.
The kind of problems that the article points out are easy to spot. If small syntax changes yield significant output code, that will be immediately reflect in the program execution.
Plus, a decent code base will have unit testing in place, which should catch more intricate, harder to catch, unwanted code flavors.
> Plus, a decent code base will have unit testing in place, which should catch more intricate, harder to catch, unwanted code flavors.
That's rather optimistic.
I just want to throw out a positive experience with it and to say that I don't support the author's premise of "here's some misleading things you can do with CS, therefore CS is unreadable". This applies to all languages, and 'fanciful' features and syntax should be avoided almost always in every language for the sake of readability.
There are MANY reasons not to use coffeescript, but JS is all functions all the time, and -> is my best friend.
result = _.map object, (val, key) ->
foo(val)
is much cleaner than var result = _.map(object, function(val, key) {
return foo(val)
});
I think.Although it is all subjective aesthetics, CS shines when paired with underscore.js, and really all "callback-as-last-argument" paradigms, ie:
_.each items, (item) ->
# do stuff with item
# ...
(edited formatting, also what alec said below :)Any time you are writing code that is ambiguous, use what you need to in order to clarify it. Don't omit punctuation just because you can if it makes the code less readable.
action(true,
option1: 1
option2: 2
)
Using empty parameters in a function declaration is even funnier; you're adding punctuation that makes the code more vague. Just omit it! doSomething -> 'hello'
doSomething(-> 'hello')
The third example is totally valid, and one I completely agree with - omitting parentheses on inner nested function calls makes for unreadable code. Don't do it.The fourth example - complaining that inconsistent indentation produces inconsistent results - is just incomprehensible to me. When you're using a whitespace-delimited language, you'd best pay attention to your whitespace.
Ambiguities with optional curly braces are certainly nothing new to Ruby developers. Convention is that only the last hash in an argument list may have its braces omitted (or more stringently, a hash may only have its braces omitted if it is the last parameter in the list). Makes for finely readable code. Don't write unreadable code.
One of the fundamental rules you should follow when working in any language is "don't be clever". Optional parentheses can make for very clean code, but if you try to get "clever" with them, you end up with ambiguous code. List comprehensions are awesome but if you use them when there's a simpler construct you can use, you're doing it wrong.
You can write bad, unreadable code in any language. Part of maturation as a developer is learning to write readable code, not just working code. A bad workman blames his tools.
> Optional parentheses can make for very clean code, but if you try to get "clever" with them, you end up with ambiguous code.
Seriously? Using extra parenthesis to add clarity is being clever? I always thought it was being safe.
The OP had a fine example of "doing it wrong", with the nested inner function call without parentheses. While technically valid, it's bad code because it's difficult to read, even for someone accustomed to optional parentheses. Such a construct is "clever" when it shouldn't be.
It's akin to programming C and everytime you write a function, if statement or for loop you have to ponder whether it looks best to put the opening brace below the line or at the end of it. I hate reading code with an inconsistent brace placement style like that.
Yes, you can write bad code in any language. That doesn't mean that languages don't have an impact on the ease of writing good and correct code.
Style guides and best practices exist for every commonly used language, and not because of flaws in the languages themselves, but because unless there is literally only one way to do things, a dozen developers will come up with a dozen ways of solving a problem.
Yeah, that didn't work out so well.
There's a difference between style guides offering a well-lit path to follow, and a language having a large number of possible expressions that are off limits in practice because they lead to bad code. The former marks out a path, the latter indicates a minefield. When the language's design actually facilitates the creation of bad code, not just by offering the possibility of dangerously ambiguous expressions, but by touting that possibility as a plus, that's a flaw.
Maybe accepting that flaw is worth it because the expressiveness of the language makes it a net plus; doesn't mean it's not a weakness or flaw or a point to be cautious about.
Coffeescript is a very flexible language. Its flexibility lets you do bad things with it, but the flipside of that attribute is that you can do very good things with it. I don't think that it's either "a path or a minefield"; I think those are two sides of the same coin. Optional parentheses and whitespace delimiting can reduce visual clutter, but if you abuse them and reduce it too far, you increase cognitive load on the person that has to read and maintain the code.
The more stringent the language, the less guidance you need in the form of style guides and community standards, but that's simply because the language itself imposes those standards on you as a byproduct of disallowing flexibility. It's absolutely something to be cautious about, but it doesn't make the language inherently flawed.
The C style syntax situation with optional if brackets is almost universally considered a flaw:
if (foo)
st1();
st2();
This is not the flexibility to do hard things. It does not save a lot of typing or make some other common construct less cluttered. It is just a defect in the grammar that it's too late to fix.It sounds like Coffeescript has more of these flaws.
if (somethingBad)
return NULL;
restOfBody();
And I find that the braces are often just unnecessary visual noise. Of course, there is always the issue of people adding extra statements, but I haven't found that to be a problem in practice. YMMV though if (somethingBad) return NULL;
That's a clear visual indicator that the braces have been skipped, but it's basically impossible to look at the next line and not know that it's NOT part of the conditional. Also, if you're having trouble fitting it all on one line, it's a good cue that you're not in a situation where skipping the braces is safe.My own opinion is that syntactic flexibility does have value (e.g. Haskell). I think it's also vastly overrated relative to that value; syntax is incredibly easy to bikeshed. Semantics require you to know something about the languages at issue.
That said, my opinion is that this kind of syntactic flexibility offers dubious value if the trade-off includes not just obviously broken programs but also subtly broken ones with errors which are difficult to spot. Newbie errors are part of the learning curve, but these seem like mistakes even experienced people can make.
The problem is that there is a local maxima in the flexibility cost-benefit curve. After you reach that maxima additional flexibility degrades your experience.
For my personal projects or small teams I'd have no reservations using CoffeeScript if it felt like the best fit. For large teams, I'd reconsider.
No, it's "If a language that provides for many ways to write terrible code (incl many ways to write the same code), people will write hard to read code." For example if parens are required, it is impossible to "get clever" with not using them.
> A bad workman blames his tools
Great workmen evaluate and avoid bad tools.
A prime historical example being Perl. A second one being PHP.
There are some annoying parts of CoffeeScript. If your function takes callbacks as the first parameters, and another value as the second (such as setTimeout), you end up with some really awkward syntax. I've seen a lot of people define a delay method that swaps the parameters of setTimeout just so it's easier to use with CoffeeScript.
Like the author of the article, I have a Python background, but I've also written a lot of JavaScript, I love the syntactic sugar that CoffeeScript brings, but I hate reading CoffeeScript code. The project that my re-implemented email editor is used in has a hybrid of JavaScript and CoffeeScript code, and I'm not kidding when I say that everyone (myself included) groans when they have to work on the CoffeeScript parts.
That said, I've seen the same problem myself.
> If your function takes callbacks as the first parameters,
> and another value as the second (such as setTimeout),
> you end up with some really awkward syntax.
Hopefully no more awkward than the equivalent call in JavaScript. setTimeout (-> alert "later"), 300
Versus: setTimeout(function(){ alert("later"); }, 300);
Or, on multiple lines: setTimeout(->
alert "later"
, 300)
setTimeout(function(){
alert("later");
}, 300); foo(a, b, c) // functions
(a + b) * c // order of operations
Making parenthesis optional for function calls means parenthesis are used instead for just controlling order of operations.In practice, CoffeeScript can't always guess when something is a function call (such as a function call without arguments), so you end up mixing in some function call usage no matter what.
setTimeout -> (
alert "later"
alert "later"
alert "later"
), 300
to setTimeout(->
alert "later"
alert "later"
alert "later"
, 300)
I think it's much more clear and close to how we write JS code: setTimeout(function() {
alert("later");
alert("later");
return alert("later");
}, 300); flip f = (a, b) -> f(b, a)
and use it inline: flip(setTimeout) 300, -> "it's fun!"
Sweet ;)for the sake of clarification, ryan is sitting right next to me, writing coffeescript (as with all new code we write here at instructure, see: https://github.com/instructure/canvas-lms/tree/stable/app/co... )--and loving it. whatever "crazy gotchas" he found back then are obviously trivial to how much easier coffeescript makes life. it seems like every third tweet he makes is about how much he loves CS now: https://twitter.com/ryanflorence
Other than that, everything in this post just comes down to "just because you can doesn't mean you have to or should." disambiguate if helps make things readable
Many people (including me) tried CS and back to JavaScript.
It's all about readability, team size, and choices.
I would say ActionScript, but FalconJS hasn't been released yet. There are other projects (such as roy) that are more functional in nature as well.
I was a flash developer way back (2003), I hear you.
My IDE (phpStorm) helps quite a bit with this, currently - provided I document everything well - but I wouldn't mind having a compiler to smack me in the face when things are wrong.
I've been using GWT solidly for the past two years (not my choice for the project, but sometimes you just have to play the hand you are dealt) - and IMO GWT's biggest problem isn't so much that it is Java, but rather that it is Java without any dynamic features (i.e reflection, late binding etc.)
I could rant at length about the dark alleys that GWT's overemphasis on static analysis can take you, just a massive hairball of impenetrable compile-time code generation in order to provide features that would be trivial if you could just reflect on things. Ironic, given the fact that your code is actually running in Javascript.
So while I don't believe static typing is inherently a bad thing, you can definitely have too much.
When developer writes some fancy one-liner in CS or Python and I'm reviewing his commit, I just ask him to come over and explain what this thing is doing. Sometimes it takes more than 20 second to read and explain even by author. Then I say: "You wrote this line an hour ago and it's already hard for you to understand what it does. Imagine you'll need to change it in a month. You'll hate yourself. Go and rewrite this code explicitly to help yourself in the future."
If you force yourself and teammates to write explicit code, you get all the pluses of CoffeeScript avoiding bad readability.
Particularly, if one line has lots of commas, I put parens all over the place because no matter what I do, having to manually parse the comma arrangements and figure out what's nested and what's a function does not go quickly. I also find myself writing explicit returns somewhat frequently just so it's much more clear what's being returned.
The overarching issue is that the syntax blurs rather than pops -- there are too many instances where things that are significantly different in function look nearly the same.
action true
option1: 1
option2: 2
If you really use CoffeeScript regularly, that isn't confusing. I don't usually write it that way though. I would do this: options =
a: 1
b: 2
action true, options
doSomething () -> 'hello' just isn't valid code.Usually I would write
doSomething ->
return 'hello'
or maybe doSomething -> 'hello'
If you write it the way he did, the parenthesis are confusing.For this one:
action = (token, i) ->
@tokens.splice i, 0, @generate 'CALL_END', ')', token[2]
-- first of all, you always want to indent two spaces. But the main problem with that is in CoffeeScript you do need to use parenthesis after the first call because your code will be unreadable otherwise. You just can't write it the way he did. moveTo 10,
20
10
doSomething 1,
2
3
4
doSomething 1,
2
3
4
Those are ridiculous examples. No one does that. You just write moveTo 10, 20, 10 doSomething (->
'hello'), 1
Indentation is significant in CoffeeScript, just like it is in Python. Just having a parenthesis shouldn't change that. Anyway, what I do is this: somethingDo = (ms, func) ->
doSomething func, ms
and then I can just write somethingDo 1, ->
'hello'
action(key: value, option: value, otherValue)
You don't write it like that if the function takes two objects. You would probably just write action {key: value}, {option: value}, otherValue
x = {
key1
key2
key3: value3
}
This is called destructuring assignment, and its part of the new ECMAScript. Its useful to not have to repeat key1, key2 everywhere when the variable with the value has the same name as the object property. The extra curly braces are just necessary to differentiate from the normal syntax.Sometimes yes/no or on/off are more readable than true/false. That's an advantage.
x = 1 if y != 0;
Don't use semicolons in CoffeeScript. I also don't put if statements at the end of a line because I don't believe that is very readable for most people including me. Also, in CoffeeScript its probably better to use isnt instead of !=, although rather than !=, you would want !==. if y isnt 0
x = 1
I'm not that sure about the use of unless, although it probably is a little bit more readable overall. break for [tag], i in @tokens when tag isnt 'TERMINATOR'
@tokens.splice 0, i if i
He says that is supposed to delete TERMINATOR from tokens. I tested it, it doesn't do anything, and the second example of the correct way doesn't work either. I think he meant this: filtered = []
for token, i in tokens
if not (i is 0 and token is 'TERMINATOR')
filtered.push token
i += block.call this, token, i, tokens while token = tokens[i]
Another example of something you CAN do in CoffeeScript but shouldn't. Actually it is a bad idea in general. I think a CoffeeScript programmer would actually write something like this: class Parser
block: (token) =>
@currentNode.push new Token(token)
parse: =>
for token in tokens
@block token
I would never write something like 'mainModule.moduleCache and= {}'Instead of
js = (parser.parse lexer.tokenize code).compile options
I would write tokens = lexer.tokenize code
parsed = parser.parse tokens
js = parsed.compile optionsIn CS, assign everything to a var, because good luck parsing CS tokenization.
In JS, assign a few things with a keyword or explicitly to a namespace, because good luck with global state(s).
Unfortunately, only the later is recommended, while the former is NOT and discouraged in lieu of being so verbose like the later. sparkles
You don't always have to assign things to vars.
If you have two or more function calls on the same line, use parentheses after the first one. Or use parentheses in all of your function calls. Its just an advantage for some people to be able to leave the parens out because it is a little more readable for them. Otherwise, you can just write it the same as you would in JavaScript.
Scope is another big advantage of CoffeeScript.
Same goes for parentheses "here or there but not there, sometimes." This may very well be the anathema for those that don't understand the claims that ";" or "function" or "}" are real burdens.
To make the OP assessment of CoffeeScript fair we would have to apply the same logic to Javascript its self. If we looked at any minimised JS code we would have to conclude that the language is unreadable.
Isn't this precisely the problem? Why make a better Javascript then make the same mistakes as the original? One of the main goals is for Coffeescript to be more readable that Javascript. Having ambiguous syntax and so many different ways of doing things means that people will write code in all of these different ways.
Your rebuttal is a long list of what you SHOULD and SHOULDN'T do, but there are two problems:
1. Not everyone will agree with you.
2. People will write it the "bad" way anyway.
Ultimately this means less readable code, especially when you have to read someone else's code.
People will write bad code in every programming language you give them. I don't see that as a justification for limiting the expressiveness of a language. If you take that line of reasoning too far you end up with Java.
That's a straw man. I never said that the expressiveness of the language should be limited. There are plenty of languages that are just as (more?) expressive than CoffeScript without the syntactical ambiguity.
> If you take that line of reasoning too far you end up with Java.
That's the slippery slope fallacy. I am in no way suggesting that CoffeScript should have completely rigid syntax. Surely there is a happy medium between CofeeScript and Java. There are plenty of languages that live there.
Is it really more expressive just because there are N ways to do it?
Even python's critics would admit it's a very readable language, and it would be unusual to claim it's not expressive.
x = 3;In short, it was badly designed. Pointless to use it.
doSomething () -> 'hello' just isn't valid code.
Is valid code. Not that you'd ever actually want to use it: coffee> doSomething = (fn) -> if fn then fn() else (-> 'hi')
doSomething = (fn) -> if fn then fn() else (-> 'hi')
[Function]
coffee> doSomething() -> 'hello'
doSomething() -> 'hello'
'hi'
coffee> doSomething () -> 'hello'
doSomething () -> 'hello'
'hello' > doSomething (->
> 'hello'), 1
> Indentation is significant in CoffeeScript, just like it is in Python. Just having a parenthesis shouldn't change that.The point, I think, is that the example above should be an error instead of compiling into something you wouldn't expect.
Here's a syntax I've seen a lot:
doSomething ->
'hello'
, 1
or even doSomething ->
'hello'
,
1
That style is also handy to construct a long list of object literals.> This is called destructuring assignment,
I think destructuring assignment is something else entirely. http://coffeescript.org/#destructuring
> its probably better to use isnt instead of !=, although rather than !=, you would want !==
'!=' is equivalent to 'isnt'. And '!==' is not CoffeeScript at all.
Er... in Python, having a parenthesis does change that, within parens Python uses continuation lines which are not subject to the same restrictions as free-floating code.
In 1948, Edward Murphy, Jr. an aerospace engineer, was working for the Air Force to install sensors to test g-forces on humans during deceleration. There were two ways to wire each sensor, and one of them was wrong. Someone had methodically installed every single sensor backwards. Murphy quipped something -- no one is exactly sure what it was -- which was cited in the Aviation Mechanics Bulletin as "Murphy's Law: if an aircraft part can be installed incorrectly, someone will install it that way." A few mutations later and this became the famous "Murphy's Law".
I think the original Murphy's law applies with great force to the world of programming languages. If there are multiple ways of doing something, and some of them are stupid, you still have to know all of those stupid methods because some idiot on your team will do it. The probability of this happening increases as the size of your team increases.
The proper course of action as a language designer is to make sure that foolish methods are not built into your language. What you do not do is design foolish methods into your programming language, and then put up a big sign on Hacker News saying "don't do that".
The Original Murphy's Law is so critical to the design of systems -- languages, APIs, libraries, etc., that after your post I feel compelled to ask you to put down your Programmer's Card and step away from the keyboard at this time.
Since you are obviously an expert in programming language design, I am curious to know, what programming languages have you designed?
He made it flexible in order to appeal to people who want a functional programming style and like to be clever. I don't want to deal with that type of code especially but some people actually prefer that way if that is the way they have been programming for a number of years or if they are just looking for something new.
"Put down your programmer's card" -- very uncalled for.
Any language can be abused or used in styles that are difficult to read or maintain. Almost all languages that I have seen have more propensity for that than CoffeeScript.
This one of the best language designs ever because of the significant whitespace (which to me is logically the most obvious rebuttal to your comment), small amount of required punctuation, intuitive and practical keywords, solving many of JavaScript's problems, appeal to Ruby enthusiasts and functional programmers which represents a sizable chunk of the best software engineers in web development, and compatibility with JavaScript. In order to do all of that it needs to have the flexibility that we are discussing.
CoffeeScript allows for the closest thing to my pseudo-code that I have seen.
Anyway I have spent way too much time on this discussion already.
...
> Since you are obviously an expert in programming language design, I am curious to know, what programming languages have you designed?
...
> Any language can be abused or used in styles that are difficult to read or maintain.
These were three classic fallacies in a row.
As to the second one (ad hominem demands of authority): an in argument, it is never classy.
That being said:
> "Put down your programmer's card" -- very uncalled for.
That was meant as a joke but I can see that it might deservedly cause offense. And so I rescind it and apologize.
When reading js, seeing the "var" really helps me to know the programmer's intent that this is a new variable being introduced, not an attempt to reassign one that should already exist. Does CS have features that make this point mute?
Yes. I've come to see it as a mistake in Python and Ruby both, and I'm definitely not pleased by its usage in CS. Even less so as it uses the same rules as Ruby's lambdas without the stops of its methods and global scopes.
This and that are completely orthogonal though, I've been using Python for 8 years and Ruby for 6 so I'm used to implicit scoping. Doesn't mean I like it, or find it a good idea to use it in new languages (I don't).
And the issue is actually bigger in coffeescript than in ruby and python both, interestingly because (through javascript) coffeescript's scoping is much more regular than Python's or Ruby's.
The only language in which I don't know implicit scoping to be an issue is Erlang, because it uses bind-once and immutable structure semantics.
> also because it takes away the nasty surprise that JavaScript gives you if you leave 'var' out.
That can trivially be fixed by linting, or using strict mode. That's really no argument.
Can you provide an example in js where knowing whether it's a new var vs. reassignment is useful? I can't think of a case, but it could just be me.
Its all about syntax and semantics. Code from languages with a small and consistent syntax, one-way-to-do-it, and a easy semantics are easier to understand than languages with a lot of semantics.
For example, Scala is cool. But also overly complex. The same holds for C++. But languages like clojure are very simple to grasp and the semantics of these languages are very small.
Coming from Python [...]
For example, in C-like languages, you can omit curly brackets
after a conditional expression if you only have one statement:
if (condition)
action();
But what happens if we add a new statement:
if (condition)
action();
action2();
Of course, that doesn't seem to be an issue in Python, the language he's coming from.Most of his points are similarly contrived and could be equally applied to Ruby, a language which doesn't require parentheses and quite a lot of developers seem to like, or Python which is similarly whitespace-dependent and quite a lot of developers also seem to like. But inflammatory headlines do drive traffic from HN...
Given that CoffeeScript doesn’t fix any of the fundamental problems of JavaScript
I think most developers who are well-versed with both JavaScript and CoffeeScript would disagree that CoffeeScript doesn't fix any of the problems with JavaScript. It certainly makes it easier to loop through object properties. It makes code more readable in a number of ways; post conditions, list comprehension-like syntax, simpler loops, the existential operator (`foo?`), and the maybe object-like syntax (`foo.bar?.baz?.quux`) are definitely improvements.You can of course write bad code in any language, no matter how structured it is. I myself am about to embark on refactoring a massive, terribly-constructed Python system that processes millions of requests each day. The author didn't follow the strictures of the Python community at all. How is that any different from someone who exercises poor judgment when writing CoffeeScript? It's not.
if(something)
doSomething();
And: if(something)
doOneThing();
doAnotherThing();
Both look like they should execute the same way - running the indented code if the condition is true. The reason this is a problem in C and not in python is that in python, this code operates as it intuitively looks, while in C, it does not.(It can trip up experienced users if they're not paying enough attention and their mind just assumes the curly brackets are there.)
* Humans use indentation
* The Compiler ignores indentation
* The indentation and the the compilers interpretation are different1: What is the JS that CS was generating?
2: What is the JS doing?
Maybe I just didn't give it enough time, but it seemed quicker and more reliable for me to just suffer through the JS syntax.
This is true for both CoffeeScript examples he gave and their resulting JavaScript.
How easy or fast it is...
- to write what I want to achieve
- to write something that makes me feel creative
- to write something that makes me feel tidy, organized
- to write a syntax error
- to write a logical error
- to step into a "gotcha"
- to find that bug
- to grasp a short snipped
- to grasp a 20K loc app
- to read my own code 1 week later
- to read my own code 3 years later
- to read somebody else's code
Now trying to rate languages by these parameters is a tough job that I can't even imagine how to tackle. In many instances it doesn't even has to do with the language itself, but rather with my experience and relationship with it. But any exercise in this direction gives some food for thought while trying to find the right tool for the next job. Or something to think about when looking back at some of the choices I've made through my programming lifetime.Although, 20k lines of Python is probably doing much more than 20k lines of Java.
Personally, CoffeeScript has been my favorite thing to come about in the past few years.
and that's all that matters to me
While nowadays I prefer coffeescript to js, I would rather have some syntactic sugar like scss is for css. I've looked into it but nothing has enough momentum to make me sure it will still be around in two years or so.
As a sidenote, while I mentioned I prefer CS, have you ever tried using a ternary operator? or pass a callback function in jquery (ex. the hover out event)? or just return "this". There are so many things that feel so wrong.
x = a ? b : c
or x = if a then b else c
And for the last one: function() {
// doing something
return this;
}
->
# doing something
@
Don't get me wrong, I love the fact that I can do nice stuff such as callback?() and some other nifty tricks, but while I'm still having fun using coffeescript, in my experience it's not that great as most people make it up to be.The reason why it works in the first place is for consistency ... in every other context, you can use an `@` to replace a `this` or `this.`.
And it's not like there's a warning about it being removed on coffeescript.org where it's being mentioned as an alias for this in the first place.
It's always good to look at the pro/cons before choosing a language, so I think discussions like this is good.
That being said, some of the problems this article points out can be addressed by going back to more JS style.
e.g. Not relying on implicit parentheses/commas/braces
I like the "Fancy" for loop because it's closer to what I do in Python (yes I know they are not exactly the same). Same thing with the "Tricky" if statements.
Also the redefinition shortcuts provided in CoffeeScript is pretty much what I do in plain JS anyway.
e.g.
JS: foo = foo || 'bar';
CS:
foo or= 'bar'"Conditional assignment of previously undefined variables a or= b is now considered a syntax error."
It's useful for something like:
f = (options) ->
options.foo or= 'bar'
# ...Once, before banging out a bit of complex code, I thought I'd write out some psuedocode to help wrap my mind around what I wanted to do. I type out my psuedocode algorithm, realize I've written valid CoffeeScript, then uncomment it.
That's the experience that made me think CoffeeScript is MORE readable than JavaScript.
Normally I don't do the spelling/grammar thing, but it was consistently wrong in the article, and at least a half dozen comments here were getting it wrong, too, so I figured a public service announcement was in order.
except when APIs get 100x bigger, API design becomes 100x more important. and using a language ecosystem that encourages good API design, over a language that is cobbled together and has no particular opinions about what good API design looks like[1] (or, even, bad opinions[2]), so that less experienced developers end up with decent output, is that much more important than concerns like parens, commas and braces. curly braces never made a project fail. shitty API design does.
[1] javascript! [2] java!
sheesh. there are bad things about coffeescript, but this stuff hardly matters.
title = getTheTitle()
img = (shouldWeGetImg) ? getImg() : "placeholder.jpg"
body = ...
# 8 lines of code to define body, some that reference title and img
# this:
obj = { title, img, body }
# instead of:
obj = {title: title, body: body, img: img}I personally don't get excited by that prospect. Generators would get me excited, though!
made code a lot easier to debug. and yes, consistency wasn't the only thing. i would say that converting over has a learning curve sure reading other people's code. but sacrificing readability long term compared to vanilla js? i wouldn't say that. i'm not saying there isn't ambiguity in the beginning but eventually you learn how to not do things in ambiguous ways.
That is not enough, because unfortunately, there are more programmers in the world than "you", and I have to use their code and / or work with them.
When language has X options their is X! opinions on "consistent".
Javascritp + ruby = coffeescript
coffescript + perl = coco
coco + haskell = livescript
So i think coffeescript is not the end of the line, and i think this mean, there is a serious need for a better javascriptThere are no absolutes. This is all subjective.
"OMG it's change, run away!"
Couldn't agree with you more. Readability is measurable and considering that a lot of programmers come from C-type languages, I really don't see why people try to modify an existing formula (as badly implemented as that formula is in JS).
tl;dr: Let's face it, if you're using Coffeescript or similar because there are too many braces or parenthesis, perhaps you should quit development right now and take a class on basic algebra, quit that and save the world your horrific code. Seriously.
Do not confuse readability with familiarity. Also, don't forget that readability is a function of both syntax and semantics.
Also, I really don't know what connection there could be between aesthetic preference and quality of code produced. Are you aware of some study that show that kind of connection? With conclusion which says that if I like line noise then I am a great programmer, and if I like whitespace I am bad? If not then your 'tl;dr' is just stupid, sorry.
Some would disagree: http://paulgraham.com/pypar.html