There has always been a strong "case against using CoffeeScript", from day one. Like any other new language, it's a new, strange thing to learn, there's new tools and a new compiler, and your team doesn't already know it.
In addition, CoffeeScript has it's own distinct burden to bear: the entire point is to compile (in as straightforward a fashion as we can manage) into JavaScript. That you're debugging JavaScript when you run CoffeeScript in production should be no surprise -- that's the point. But it's also quite a large hurdle to jump.
What's interesting about CoffeeScript is that despite there being a strong case against using it -- and I try to make the same case in my workplace -- people still find it useful enough, and transparent enough, to use it regardless.
As of this morning, CoffeeScript is the 13th most popular language on GitHub, and the second most popular module in npm, after Underscore. All of the top ten languages on GitHub date from the mid-1990s or earlier.
The better question is not "What is the case against using CoffeeScript?" ... but instead: How is it that despite all of the obvious hurdles stacked against a new source-to-source language, people are actually using CoffeeScript?
That's the question I'd like to know the answer to.
With JavaScript ordinary developers hit a wall at some point and can't make further progress. Like Java has shown, how good the language is seems secondary to how good the implementation, the runtime, is.
JavaScript runtimes have kept on improving and with the V8/Node.JS combination have started taking over the server-side code.
With careful engineering, developers can get even further with just JavaScript the language. I wonder for instance whether using the Google Closure Tools can bring more gains there. Such tools help with checking the code for common pitfalls and the style guidelines also help a bunch. Plus, they might help with deployment, with the packaging for the specific program you've written so you don't need to package up all the library with it. Then you don't need to feel ashamed of making use of lots of library code, if only the part of it that you're using gets deployed.
Still, working with JavaScript directly can also bring debugging challenges, even more when you're trying to debug code that has been mangled since leaving your library source files.
Languages like CoffeeScript and Dart have success because the alternative in pure JavaScript is still quite painful, as I've mentioned above. It's a mistake to dismiss languages like CoffeeScript and Dart because you want that 50% extra performance.
Still, CoffeeScript is a concept. It could be said to have been over-engineered in places. Some of the stewards of JavaScript would have found it better if it was more like a JavaScript with JSLint turned on and some niceties on top of it.
Just to clarify one thing, it's very unlikely that you'll take a 50% performance hit when moving from JS to CoffeeScript. That may be true for Dart as well--I don't know.
Also, since CoffeeScript transcompiles to JS, it will generally reap the benefits of future improvements to JS runtimes, packaging tools, etc.
It's a ton of fun to write. That's why. I absolutely love writing it. That alone is why I didn't pull the plug on it at work.
I have great feelings of guilt for posting that article w/o talking more about the things I really enjoy about the language. I'm having blast with my SnackJS CoffeeScript branch. https://github.com/rpflorence/snack/tree/amd-coffee
I think every JS developer should give CoffeeScript a spin in a side-project.
Heh, you're framing the question in a way that is more likely to elicit positive CoffeeScript comments, but I'm happy to oblige.
In order to adopt any new language, you obviously need to eventually believe its benefits are greater than its hurdles.
CS had a few hurdles for me early in the process. I had to install it, and that was simple. Then I had to learn it, which was fairly easy. It helped me that I had a fairly strong foundation in Python, Ruby, and JS, and the subset of CS that I initially learned is largely a blend of those languages. The language is small and well-documented. (Hopefully we keep it small--there's a bit of creeping featuritis in the community.)
Debugging is the only ongoing hurdle. It's never been a big issue for me. Obviously, I try to write the code correctly in the first place, and CS's nice syntax helps me focus on writing correct logic by removing the need to deal with JS boilerplate code. When I still manage to write incorrect code, I use all the debugging tools that I would use to debug JS. Someday my ability to write large JS programs may atrophy, but I'll never forget how to read it or make simple modifications in the debugger.
Even if the hurdles of CS were relatively minor, it would be pointless to use CS if it gave me no benefits. For me the benefits of CS are its writability and readability. It's that simple.
I'm already sold on CS, but I could still suggest ways to minimize the hurdles. First, keep the language small and simple. Second, no matter how good the current docs are (and they are pretty good), continue to refine them. Finally, do everything possible on the CS side to facilitate debugging, which mostly comes down to line number support.
I think the blog post was a good one - it pointed out the problems.
Instead of defending it - go make it better. Address the items put forth right ?
1. work to get native debugging
2. clean up the looping mechanism
etc...
Instead of taking this guys post and saying 'negative criticism', see it as 'positive criticism' - he is reviewing it, showing what can be better, showing the obstacles.
I think part of developing a truly good library, is the ability to improve it, take criticism, expand and grow - not to cling to the original concept and defend all day long.
Personally I'd concentrate on the debugging, secondly, I'd work to improve the overall syntax, maybe providing alternative, more structured for some - others that don't like that can continue to use the same.
But again, rather than everyone just spamming here on how it 'rocks' or how it 'sucks' - would be better to take these observations and go and say 'how can we improve' - this is golden feedback here!
Are there examples of large(ish) projects or production websites using primarily CoffeeScript?
It seems like CS has a fanbase among purists who enjoy the syntactic elegance. But in more pragmatic environments the extra pain in the develop-debug lifecycle would seem to be a showstopper. I just wonder how many developers end up voting with their feet when confronted with that choice in the real world.
P.S. Another argument against that wasn't mentioned: CoffeeScript represents an additional barrier to entry for opensource projects. Shipping compiled JS code is not unlike shipping a compiled binary... and if you want people to participate and contribute, requiring CS knowledge is an extra hurdle that most JS devs will shy away from.
In my case, Rails 3.1 is the answer. I never felt the urgency of a better JS so much as to look for solutions, but having it already integrated into the workflow makes you really appreciate it. Except when debugging, of course :)
those who care and think that they need an abstraction on top of JS / feel like it's a good approach / can't understand or find it hard to use plain JS, are free to use it.
I think people can have positive or negative opinion about things. deploying application frontend of which was written in CoffeeScript was the biggest mistake in my entire career, but I don't blame coffeescript, it was me stupid choosing a technology that doesn't suit the task.
people use many libraries that are generally bad, and asking "how do people still use it" and claiming that it's super-popular is very lame and doesn't really explain anything, and never is a counter-argument.
i don't like it, that's my opinion, it didn't work well for our projects and we have rewritten most of things, and will rewrite the rest of CS back to JS.
it's ok that people use it. some of them will face same issues we did and come to same conclusion. others will be happy, since it solves (THEIR) problem. the rest will never be smart enough to grasp why (and wether) it's good or bad. it's trendy and looks cool, so they'll stick to it.
you've done a great job when created it, but our problems are still better easier and more stably solved by plain js.
there were many problems with Barista during deployment. Some things that worked perfectly in development somehow stopped working later on, because of required recompilation. We were deploying in a high-security area, where there's no internet connection, and having to install Node or JS runtime was not good. But again - that was my stupidity, did not foresee that.
moreover, barista did not recompile assets until we touched / saved / modified the file.
We do not have direct access to production system, so all the bug reports are sent in JS, and we need to figure out how things happened and sometimes it was quite difficult to locate a precise place where it occurred.
now, we have to compile everything and package js files when preparing an updating version of software (again, no SSH connection, no internet connection, only tarball / zip update packages).
and, in general: even though everyone says that CS handles everything that JS does plus more, we found that it's easier to handle factories, mixins, scopes, inheritance and more with plain JS. it was always easier to read and understand, and no overhead for development or production, felt more natural. classes are not something we missed in JS, and comprehensions stuff seemed more like a syntax sugar, not something we really missed.
So - it added a complexity and made people to use CS features instead of things that fit more naturally to JS ecosystem. We've had several issues with syntax (e.q. hash assigned to a variable with a function inside or smth like that), but we worked around them on early stage.
The debugging complexity is more than just a fair point. It is what kills the language for me.
I spend way more time debugging code than writing it. I suspect most coders do. Programming has taught me that I am far from perfect.
Give me a language where I can jump right to the problem and my debugger will tell me how I goofed up, and I can look at it as a potential language I will use. Give me something where you double my debug time (which will add probably 30% to my total programming time) and I will ignore it. It doesn't matter how pretty and short it makes the code. Give me error line numbers or go home.
If you make my debugging more of a chore than it already is, you ain't no friend of mine.
I also agree with the author on the need for symbols rather than words in languages. It may make people feel all warm and fuzzy to write what looks like english, but && trumps and every day. {} or () wrapping something gives me a lot of info about what is going on and with little mental parsing.
In the end, CS just doesn't deliver a advantage to me that is worth moving away from something as easy to code in as JS, warts and all. And I have tried very hard to want to like it.
I've been working on a large project in CoffeeScript for about 9 months now and have never noticed or felt burdened by the translation between JavaScript source line to CoffeeScript source line in debugging. Maybe it's different for others, but the code is so structurally similar that there shouldn't be a major difference. It's not like looking at the assembler output of a C++ program and having to demangle names and deal with compiler-generated labels and jumps etc.; the structure of the program is still the same, it's just the syntax that's really only mildly different.
Of course, though, it comes down to personal preference. Don't want to use CS? Fine! Great! Go hog wild! No one's forcing you to. Me, though? I'll never go back.
I agree with the author of this post. What is interesting for me is how people are happy to use an intermediate layer in front of javascript just to gain some syntax and not real semantics.
CoffeeScript does not really change the complexity of your program. I agree that in modern languages it is a good idea to avoid all this parens and useless syntax that we have in Javascript, ObjectiveC, and so forth, and I hope next versions of Javascript could even change syntax at some point, but to use an intermediate layer/compiler/translation stuff just for that... is strange.
There are real semantic differences in CoffeeScript. As listed by jashkenas himself more than a year ago in response to the same accusation:
Leaving features off the table and just talking about semantic cleanups, here's a few:
* Switch statement doesn't fall-through by default.
* Variables don't need to be declared with "var", and can't ever become global accidentally.
* Equality is strict, using `===`, if you want coercive equality, you coerce the object yourself.
* Loops that are used to generate functions close over their index variables, so that all inner functions don't share the final value of the index. This is the same problem that ECMAScript Harmony introduces "let" to solve.
* Splats largely replace use of the "arguments" object, so you don't get bit by "arguments" only being a faux-array.
* Comparisons can be chained, as in Python -- so you can write: "100 > x > 25", and have the answer be correct.
* Multiline strings are valid without having to escape the newline.
* And the big one: everything in CoffeeScript is an expression. Without having to use temporary variables, you can return the result of an if/else from a function, pass a comprehension (loop) directly into a function call, assign the result of a try/catch, and so on.
That's definitely more semantics that I thought it contained, thanks for the clarification. However for the same reasons the debugging problems are probably more severe than I realized before.
As I said elsewhere, unless you believe the CoffeeScript compiler is introducing bugs into correct code, I'm not sure I understand your concern. Does the fact that C has different semantics from machine code generally make debugging C harder than debugging machine code? There is still a very close relationship between the CoffeeScript and the JavaScript — closer than, say, what you get with GWT or Closure Compiler. The ease of reading and writing and the reduction in source code length is worth the minor context switch to many people. It's easier to spot a bug in five concise lines than in 25 lines of mostly boilerplate.
For not using Javascript itself, that is widely understood across the programming community, that is not perfect but either not a disaster as a language, I would expect a lot more gains from a to-javascript-compiler. Here what I see is that there are a few semantical changes and syntax changes, but for me this is not enough to switch.
What I think the value of CoffeeScript could be is to show how Javascript could be improved in the next versions.
-> is Coffee-Script's greatest trick. When I first started poking through the codebase, it would take me ages to 'sound out' each line. Coffee-Script is succinct not simply because its syntax is short, but because it is dense. Everything is an `expression`, which means I can be more expressive/loc.
I can now read it as fast as any other language, which means I can comprehend code faster.
> doSomething() if five and six and seven
That was difficult for me to parse as well until I saw it enough. Which is true of everything new I have ever learned. Some syntax took me out of the C comfort zone, and I am better for it. There's a feel that code written "out of order" has that I instinctively like for some scenarios.
> Therefore it will never really be supported natively, and will always be a compile-to-JS language, and will therefore always have a terrible debugging experience.
Source mapping will be helpful, but clearly fewer people than I'd expect know how to use the debugging tools in their browser to the extent that I use them.
I don't want a line number, I want a break point or watch expression in my code while I'm debugging, I don't want to go back to my editor until I know the state of my app that's causing problems.
It is all very minor, but it piles up and slows me down.
We scan the left side of the code when reading. Checking the right side of the code to see if it actually will execute must invariably be slower. Also, the language encourages super long comprehensions.
Exactly. Sometimes you want the predicate muted so the consequence is highlighted. I agree that it seems counterintuitive, but I'm pretty sensitive to code flow and it works for me personally.
consequence is still on the left edge in my rewrites though, not sure why you'd want to mute control flow aside from reading it out loud.
Admittedly, you can still do some ugly one-liners in JS, the undertone of all my points though is that coffeescript's draw is the really beautiful, readable code, which leads to (at least for me) stuff that's harder to follow when I'm not writing it.
> This is really weird:
getUser = (id) ->
url = "users/#{id}"
dfd = $.ajax
url: url
format: 'json'
method: 'get'
url: url
promise: dfd.promise()
> It’s hard to recognize instantly that I’m actually
> calling $.ajax, not just assigning it. If you don’t know
> CoffeeScript, I’m sure the last two lines are totally
> baffling.
Um... I agree, but as someone who does know the language, that's clear, unambiguous and easy to read. I maybe wouldn't use implicit object return there, but it doesn't surprise me to see it.
In fact, I'll make a positive argument for the implicit call syntax: the whitespace under 'dfd' tells me, without reading any further, that the line is a call which takes an object argument. Without significant whitespace, I couldn't rule out it being just a typo.
These are all valid points. Debugging is probably the most significant. I agree that the syntax can potentially make CoffeeScript hard to read, but this can be easily avoided.
One of the nice thing about CoffeeScript is that it allows one to be selective about the syntax. For example, unlike the author I will use keywords like is, isnt, or, and, yes, no, on, off because I find it easier to read that way. However, I dislike the calling functions without the parentheses and I dislike the long one-line comprehensions. Instead of doing this:
eat cheese for cheese in cheeses when cheese is blue
I would do:
for cheese in cheeses
if cheese is blue
eat(cheese)
I can have a minimal syntax, but retain some of the unnecessary bits to make it easier to read. If the author finds themselves writing code that is hard to read, they should just not write it that way. Write it the way you (and your team) like it to be written.
The increase in debugging complexity is a fair point, and it does add an extra step in between finding a problem and fixing it. However, in practice, I'm not sure I've ever had this be a major problem; I've had more problems related to bad error messages from the Coffeescript compiler (generally due to significant-whitespace errors) than I have had in trying to associate Javascript to its source Coffeescript.
The rest of the examples, though, don't hold much water. The takeaway from them is "it's possible to write terribly-factored code, even in a pretty language".
doSomething() if five and six and seven
Can be rewritten as this, if it's more comfortable for you:
if (five && six && seven) then doSomething()
!=, &&, ||, (), and the like are still usable in Coffeescript. If it makes the code easier to read, then use them!
The whole concern with list comprehension syntax order is sort of a red herring - it works that way in Javascript 1.8, as well! In fact, nearly every language that implements list comprehension does it similarly. 160-column 1-liners are evil in any language, not just Coffeescript. If you don't like the "method, loop, condition" syntax of list comprehensions, that's a problem with list comprehensions, not with Coffeescript.
The points about the implicit returns and parentheses-less calls apply, as well. When it makes sense to explicitly use them for code clarity, use them.
For example, your confusing getUser method can be easily written as:
Voila, it's still coffeescript, and it's very readable, easy to understand, and not at all difficult to maintain.
The fat arrow, like the rest of the examples, is yet-another-case of "Yes, you can shoot yourself in the face with it, if you abuse it". Event binding isn't the only case when you need to preserve scope! Sometimes (frequently, even) you need to pass around an anonymous function bound to a specific scope. The fat arrow is explicitly for that sort of work. If you abuse it every time you could otherwise just call call() or apply() (because you have access to the scope to invoke the function on, in addition to the function itself), then yeah, it's bad.
Have you even looked at the implementation of $.proxy() used in your examples there? Guess what it does? It creates a new anonymous function, binds it to the passed-in scope, and returns it. Which is exactly what the fat arrow does in Coffeescript. Why is it evil in Coffeescript, but okay in jQuery? Because you aren't aware it's happening?
Your "This is how you should do it" method in Coffeescript is wrong. It should be something like this:
That's clearer than any of your examples, Just Works, doesn't have a jQuery dependency, and is even less code than your corrected example. First-class functions have the `this` problem in every language, and the fat arrow is very nice syntactic sugar for what you have to do anyway.
The purpose of Coffeescript is to make your life as a Javascript developer easier. If you're using it to make your life harder, you're doing it wrong. It's not a case against using Coffeescript - it's a case against writing terrible code.
First-class functions have the `this` problem in every language, and the fat arrow is very nice syntactic sugar for what you have to do anyway
Not true. JavaScript's `this` binding is absolutely not the only way to do it.
In Ruby:
class Foo
def hello
return proc { self }
end
end
puts Foo.new.hello.call #=> #<Foo:0x10e56c840>
In Python:
class Foo(object):
def hello(self):
def zoo():
return self
return zoo
zoo = Foo().hello()
print(zoo()) #=> <__main__.Foo object at 0x102453f90>
In Ruby, blocks carry the original `self` as part of their variable scope. Rubinius exposes this if you want to poke around:
ruby-1.9.3-head :007 > x = proc {}
=> #<Proc:0x1598@(irb):7>
ruby-1.9.3-head :008 > x.block.scope.self
=> main
In Python, it's a side-effect of the fact that you need to explicitly define `self` as a parameter.
In Ruby, it's possible to override the `self` in a block using instance_eval, just as it's possible to explicitly define a `this` in JavaScript using `.call`.
The difference is that in JavaScript, the callee is always responsible for deciding what the `this` should be (often implicitly), where in Ruby and Python, the function's original binding includes its `self`.
edit: another way to put it would be that Coffee's fat arrow fix is the default in Ruby and Python.
JavaScript is forced to make the pessimal choice of only ever having dynamic-bound "this", because of that favorite whipping boy ... prototypal inheritance.
If JavaScript had a notion of a class (even as a constructor function + prototype) definition, then it would be possible to tag methods with the correct instance reference, as in Ruby and in Python. Unfortunately, because the standard pattern for defining an instance method is:
Klass.prototype.method = function() {
...
};
... where the function is an expression just like any other, you have to have dynamic-bound this to make prototypes work correctly by default.
You should look at Lua. Yes, if you want a notion of "self" or "this" with prototypal inheritance, then it must be dynamically bound. However, this does not have to be a burden on, or even be noticeable by, the programmer. Good language design can avoid most of the pitfalls in JavaScript...
To elaborate, Lua has special syntax for defining and calling methods vs unbound functions. In Lua,
function woot:donk() ... end
woot:donk()
is equivalent to
woot.donk = function(self) ... end
woot.donk(woot)
and when you define a function normally e.g.
function() ... end
there is no self parameter, thus the one from the parent scope is captured. The flaw with JS, really, is that it forces every function to be a method, and doesn't let you express whether you are defining one or the other.
> JavaScript is forced to make the pessimal choice of only ever having dynamic-bound "this", because of that favorite whipping boy ... prototypal inheritance.
I don't think that's true: functions accessed via objects could very well be bound to the object (that's what Python does, accessing a method via an object instance returns a "bound method" which is curried with the instance, whereas accessing the same method via the class returns an "unbound method" which requires a class instance as its first argument).
That works in Python, because Python has a distinction between an "instance" object, and, say, a Dict. In JavaScript an object is an object is an object -- under your proposed change, what would be the value of "this, in this:
Absolutely. I don't mean that dynamic "this" is required by prototypal inheritance in general -- I mean that it's required because of JavaScript's particular implementation of prototypes, where they're exposed to you as a value, but there's no syntax where the language can tell that you're in the process of building one out.
You're right - but I'd argue that the difference is just in default behavior, as you touched on in your edit. Ruby procs are bound to their lexical scope, and implicitly include self as a part of that scope. The function still has to be aware of some scope to run, though.
(I didn't know about assigning self in instance_eval, though - that makes me all kinds of happy!)
What I meant to convey is that every language that uses first class functions (I guess I should say "any language with first class functions and the concept of this/self") has to deal with the problem of execution context. Languages like Ruby and Python assume the common case and bundle the context by default. Languages like Javascript and Lua don't, leaving it up to the caller to explicitly specify. In all cases, though, the callee has to be made aware of some context, which is what I meant by the `this` problem. My point is that since Javascript requires explicit binding, a language construct that allows Ruby-style implicit binding is nice syntactic sugar. The author seemed to be conveying the idea that using jQuery's $.proxy magically freed him from the need to pass context around, which is just simply not the case.
What do you mean by Lua leaving the caller to explicitly specify? Are you referring to earlier versions of Lua that used the explicit ^ upvalue sigil? Lua 5.1 (and 5.2) functions close over all local variables (including the implicit “self” introduced by function definitions with colon syntax), with the innermost ones first and no explicit upvalue sigil, much like Scheme.
Nah; regular old Lua 5.1. The colon syntax is just syntactic sugar -- self isn't actually bound.
function Foo:Bar(baz) is the same as Foo.Bar = function(self, baz); invoking Foo:Bar("rebar") is sugar for Foo.Bar(Foo, "rebar"). self is never bound - it's just passed in (explicitly, via . syntax, or implicitly, vs : syntax). In all cases, the caller is always specified.
You can pass Foo.Bar around (as it's a function reference), but if you have something like:
Foo = {}
function Foo:Bar(baz)
print(baz)
end
local baz = Foo.Bar
Then baz has no binding information to Foo; defining the function with the : syntax is just syntactic sugar. To invoke, you would have to call:
baz(Foo, "woohoo")
Just calling
baz("woohoo")
populates self with "woohoo", and the bar parameter would be nil, demonstrating that there is no contextual binding to the function itself.
What do you mean “isn't actually bound”? The function being defined using colon syntax doesn't close over self, since it's a parameter—but functions defined within that function will close over the self parameter, since it's a local from an enclosing scope:
foo = { x = 3 }
function foo:bar(baz)
return function(thud)
return thud + baz + self.x
end
end
womble = foo:bar(4)
womble(7) --> 14
Python I believe also closes over self as a variable, and Ruby has similar behavior for local procs, even though « self » is a special form in Ruby.
This is in distinct contrast to JavaScript, where the value of the special form « this » goes nuts inside closures because it's attached to the function:
Ah, okay, I see what you mean. Yeah, you're absolutely right there. Point conceded.
Mentally, I was separating Javascript and Lua from Ruby because while the caller is explicitly passed in Javascript and Lua (either via call/apply, or as a parameter), Ruby methods are implicitly aware of their scope (and can't really be referentially passed around like Javascript or Lua methods). Lua "methods" aren't aware of their scope (though closures are.
Ruby methods bound to their objects can be passed around; it's just a bit more cumbersome, in the form of « m = obj.method(:foo) » followed by « m.call(…) ». You can even do « m = :foo.to_proc » in recent Ruby and be able to « m.call(obj, …) »; this is quite useful for things like « [1, 2, 3].inject(&:+) ».
So we have that Python's dot always binds, Ruby's dot always calls, and Lua's dot is always a table lookup, with Lua's colon being a separate syntax for (always) the compound operation with injected self-argument.
And then JavaScript is the schizophrenic one: « obj.foo(bar) » is not the same as « var y = obj.foo; y(bar) ». Property lookup and function call insert a hidden step between them when and only when directly composed, and a function call without an immediately adjacent property lookup in a way injects the opposite step of making « this » in the callee be the global object (I think). “Politicians lie in cast iron sinks.”
I think part of the gist of the article was that abused CoffeeScript is more bad than abused JavaScript. So, yes, of course, you can do everything differently. But the point is, in the moment when writing it, it's not confusing, and doesn't stand out as a bad thing. Later, when you look at it, it IS confusing.
I dunno. I think that a lot of that is just a matter of developer maturity and code discipline. If you can't recognize a code smell when you're writing code, the problem is probably that you aren't mature enough as a developer, not that the language is bad for allowing it. I don't think that most seasoned developers would look at that 160-column list comprehension, say "okay, that works!" and move on to the next task. They'd say "okay, that works, but holy crap it's ugly. Quick, to the refactormobile!" I've seen 160+ column nested ternary operations in pure Javascript that make the aforementioned comprehension look positively poetic.
I've seen some genuinely horrible Javascript. Like, stuff that so terribly abuses the language without any comprehension whatsoever of closures or context binding and only works because the author got stupidly lucky and/or abused globals so badly that the code was effectively one giant function. In both cases, bad developers writing bad code are going to produce unmaintainable messes. Doesn't mean that the language should be avoided. Garbage in, garbage out.
I've seen terrible JS, but I've seen much worse CS. Most programmers have relatively no sense of organization, and when unleashed with CS create things far more nasty than their JS counterparts.
I've seen terrible assembly but I've seen much worse C. Most programmers have relatively no sense of organization, and when unleashed with C create things far more nasty than their assembly counterparts.
But you get the idea. I'm merely pointing out that compilers are tricky and complicated beasts. And especially in the rewriter CS has to jump through hoops to disambiguate.
I dont know if I would call CS complicated, but yeah you'll get that all the way down. Noobs will write their js, bigger noobs write CS, smarter people off writing C, and even smarter people writing ASM. Most will still write nasty code regardless but the ambiguity in CS makes this far more painful to consume
Unfortunately, in this particular case, the code was equally nasty even when all of those methods were written in Ruby, and has grown nastier over time through pull requests. I think it tells you far more about the author than the language ;)
Which bits do you consider to be code smells, out of curiosity? Things like postfix conditionals and list comprehensions generally aren't considered to be smells by the majority of people that use them. I don't see any 160-column list comprehensions or implicit bracketless object returns there, which are genuine smells.
That is an opinion. I like that CoffeeScript allows me to have more choice over how I write my programs. If I want to write a piece of code in a different way, I can. If I don't think it will improve readability, I won't. You don't have to use all features of CoffeeScript all the time.
Plus, if you don't like it at all, just don't use it and quit complaining. :)
I think the point of this article is that because of Coffee's wide use, developers will often find themselves in a situation where they can't "just don't use it".
You could say "don't take that job", but I think the point of the article is to make it clear that CoffeeScript is not an unvarnished win, and that choosing to use it will result in otherwise qualified people taking your advice and not wanting to work on your project.
Yes -- that needs to be fixed pronto, and a 1.1.4 release cut -- which should include both that patch and a fix for Node 0.6's inconsistent "fs.watch" behavior.
It's not confusing at all. It's the same thing your sample code does. In all three of your examples, the handler is bound to whatever scope widget is in. The code is functionally identical to your examples.
Edit: Okay, I see what you're doing; attach is supposed to be invoked with a bound scope, which does change the binding. So yeah, my code won't work in your example. I'd argue that that's more of a lack-of-context problem in the example though, since it's dependent on knowledge of the usage of the attach() function to know that that binding behavior isn't desired. Given the context of the example, I contend my solution was acceptable. :)
Fat arrow there is still functionally equivalent to $.proxy.
The arguments you use against CoffeeScript are actually the reasons why I love CoffeeScript and the fat arrow.
To see what scope the fat arrow is bound to, all you need to do is scan up the parent scope in the file and look for a thin arrow (or a class declaration).
That's a good point, and I've actually adjusted my color scheme to make fat and thin arrows different brightness, the fat brighter to call it out more.
The constructor doesn't need one, and there is a small performance hit for binding =>.
Mixins... Can you show me an example?
Times when you need a reference to a canonical function object, like with $.proxy and $.unbind. (CoffeeScript could implement => to set the original function as a property of the bound function)
was unreadable to me. $.ajax+'\n'+indent instantly triggers nesting, hence call, in my head. The way I instinctively parse this code makes the added parentheses just distractive noise to me. Actually I would have written it this way, which makes things tick even more:
A single newline makes the whole thing absolutely obvious to me, including the possibly ambiguous meaning around the implicit return.
> It's not a case against using Coffeescript - it's a case against writing terrible code.
Actually it looked like a case of change refusal and confirmation bias. The author seems to be well-versed in JavaScript, trying hard - consciously or not - to write JavaScript in CoffeeScript, which in turn produces terrible code. Every itch gets scratched, producing terrible irritation.
> you can’t compress [whitespace]
gzip. rake assets:precompile generates .gz for you so it doesn't even incur a performance hit on your servers.
> The increase in debugging complexity is a fair point, and it does add an extra step in between finding a problem and fixing it. However, in practice, I'm not sure I've ever had this be a major problem
Me neither. In fact the mapping is most of the times fairly obvious. Besides, editor enhancements[0], though not essential, helps a bit to not have to pull stuff in the browser every single time. Anyway SMAP support (or something close) is on the way[1] but in the meantime it doesn't bother me the least with CoffeeScript, thanks to it being so close to JavaScript.
Am I the only one to completely disagree with the author?
It's important not to mix everything. CoffeeScript is a language. There just happen to have an implementation for it.
This invalidates the "debug" workflow problem. Let me restate the CS debugging workflow:
Start discovering the problem in the code I wrote.
Fix it in the CS
Problem fixed, move on, otherwise start over.
A computer language is by definition a mean to express unambiguous statements. So does CS. So does the other languages. The resulting program will do exactly what your code asked to.
Do we check compiled code when we code in C? Do we look at the Bytecode when we write Java?
The fact that CS compiles to JS is an "implementation detail". That's the CS compiler's job. If you don't trust it, don't use it.
"Check out this one-liner I pulled out of the first .coffee file I randomly opened from our application:
scores = (student["assignment_#{@assignment.id}"].score for own idx, student of @gradebook.students when student["assignment_#{@assignment.id}"]?.score?)
That’s 160 columns of “readable code”..."
You can write a 160 columns of unreadable code in any languages.
If you guys enjoy coding like that, don't blame coffeescript.
You might be right however that it is "more tempting". For instance, with scheme, it's really easy to start using macro and redefining everything; or to nest lambdas into lambdas into lambdas "because you can do it and it avoids using variables".
Still, I think it's the programmer's job to write clear code, whatever the language.
Seems like author is trying to learn a new language and is having a hard time getting out of the shell he created around him with experience. During the struggle he wrote this post.
I had exactly same feelings about one-liners and understanding succint statements when I was learning Python after years of programming in C.
My code is more readable for me than yours. That’s just how it is. While the JavaScript CoffeeScript compiles looks decent, it’s still not mine.
That would change if you practiced reading other peoples' code. When I see my code, I often think, "yeah, I wrote this" because I know how I name things, but other than that, my code looks like everyone else's code. I know this because I based my style on what other people do, rather than just inventing my own. (At work, people like to write Python likeItsJavaOrSomething, and I refuse to follow that convention. While ugly, it doesn't make the code amazingly hard to read. You get used to it with practice.)
Now, compiler output is another thing, but c'mon. Sometimes I have to figure out what the computer is doing by reading memory dumps in gdb or the assembly output of gcc -O3. Complaining about the output of a JavaScript compiler is just silly; do it more and you'll figure it out. Programming is all pattern recognition, and if you can't understand patterns in the output of your toolchain, then you aren't using the tools enough.
People also like to complain about things like GHC's type inference errors or Perl's "confess" dumps. I personally have no problem extracting the information I need from them. I write a lot of programs and they fail a lot, so I've gotten good at reading the debug messages. Instead of saying, "this is too hard", try harder, and soon your life will be much better.
> That would change if you practiced reading other peoples' code.
I'm really active on github, and actually read the source code of a lot of projects instead of reading books, and contribute to a bunch of them too. Quite assuming of you to assume I don't.
My code, since I wrote it, will always be easier to understand because I dealt with every problem--not because of the style.
I totally agree with you there. There is something totally different about reviewing someone else's code versus reading your own. Because even if you wrote it a long time ago, it's still easy to reverse-engineer the purpose of everything you did.
As a year-long CoffeeScript user, I have a few points to rebut:
debugging workflow: he makes a long list of bullet points, when in practice you can find the error directly in your coffeescript code 90% of the time, since you have a clear image of what it's compiling to and errors tend to happen on the piece you're working on. Hopefully source-mapping in Firebug/Inspector will be ready in a few months and end this discussion.
"We Process Images and Symbols Faster than Words": It has been demonstrated that the understanding of text is based on the same principle, we don't "read" words, we identify them. and and or are as much a symbol as == and && (which you still can use if you think they are more visible).
"ugly syntax": the examples are ugly. Don't use a trailing if for long conditions, don't create huge one-line comprehensions. You can do worse in javascript.
if five and six and seven
doSomething()
"bad parts": every language has bad parts, but the example is just bad practice. Just because you can omit return and start an object literal without indentation doesn't mean you should.
The debugging complaint is valid, but a) doesn't generally seem that troublesome in practice (especially given the still-crude state of JavaScript debugging tools in general), and b) will be a lot less valid in the near future, as WebKit, Google and Mozilla are all adopting support for alternative languages in their respective JavaScript implementations IIRC.
I didn't mean to suggest they're not useful, just that they're relatively new and not as comprehensive as some other languages'. They're unbelievably better than the big fat nothing we had when I started coding JavaScript in 1998.
Sure, but we'll learn from CoffeeScript and apply it to the next ECMA ver. The CoffeeScript discussion is a worthy one, but declaring it unworthy to have a seat at said table prevents progress.
I'd rather read articles on how to make things better instead of bashing.
> Go ahead and [do this horrible thing], it's not _that_ bad.
Sure. Let me insert a few things that can have really bad side effects that I still think you should do:
• Leave your house every day (car crashes ROFLstomp homicide as a cause of death)
• Eat food (most foods have some ill effect on your body)
• Write some code in C (it's a very unsafe language, but sometimes necessary)
• Use a computer (hurts your eyes and causes carpal tunnel syndrome)
Long story short, everything looks horrible if you only look at the bad parts of things.
> I feel like people don't know how to use web inspector...
You're right, many people don't. I'm not one of them, but I encounter them all the time. That kind of thing is still relatively new, so it's hard to blame them as much as pity them. And of course the Web inspector isn't there outside of a Web browser context.
> There will never be native CoffeeScript, you will always debug the JavaScript.
I never said there would be native CoffeeScript; I meant they will support source maps for alternative languages. Unless the CoffeeScript compiler is introducing bugs into correct code, being able to get error line numbers in your CoffeeScript file will mean that you mostly won't have to debug the generated JavaScript.
Assuming the coffee script files are accessible over a url, wouldn't that be possible with a Firebug extension?
Which basically maps the break point to JS.
> You feel pictures and symbols, you feel the relationship.
> one !== two
> isSet || isDefault
>
> one isnt two
> isSet or isDefault
I agree, but it's important to note that pictures and symbols are only felt because the relationships have been so well defined in the past. I'm not a JS or CoffeeScript developer (I use python and C mostly), so "!==" is much less clear than "isn't".
A style guide becomes very important in a language like CoffeeScript where (a) there aren't established conventions for the language yet like Python's PEP-8 and (b) excluding unnecessary syntax is very, very tempting.
No parens or braces necessary? Don't just omit them everywhere! I was reading the source code for batman.js (written in CS, after having written quite a bit of CS myself), and some lines were absolutely baffling. It took way too much work to parse.
I've started building up a personal style guide. The most useful parts for maintaining readability are probably:
- Always keep the parens for function calls, possibly excepting oft-used functions that are only being called with one argument. (e.g. document.createElement 'div'; parseFloat '10.5')
- Always keep the braces for object literals when used in an expression (e.g. as an argument in a function call); omitting them in a simple assignment is OK.
I use CoffeeScript in nearly all my JS packages now, and I like it a lot.
But I do not like how many people use it. I use only a subset of the features. The most important aspects are: short function syntax, implicit vars, short object syntax, function binding to this, @.
I always use brackets when calling functions, because you need to use them anyway, when you do not pass any arguments. Omitting brackets makes CS nearly unreadable.
CoffeeScript is a big win for whatever I am doing. The resulting JavaScript is more stable since CS removes the possibility of a lot of flaws and giving developers the best parts of JS. On the other hand, you still need clear and constraining guidelines to manage complex projects. But you need that in any language and framework. It's not just a necessity in JS development.
Thank you, Jeremy. It pays off!
The debugging problem is definitely the biggest strike against CS, so it's weird that most of the article focuses on stylistic options (all those parens, brackets, and ampersands are optional!).
I'm still feeling CS out in terms of what style works best for me (for instance: should I always wrap arguments in parens so that all function calls have parens following them?) but it's nice to have that flexibility and be able to find what I think is readable and clean. For instance, I prefer '==' because it parses better at a glance, but I find that 'is' is much faster for me to type. :/
Also the white-space argument meaning it will always be compiled is not necessarily true. CS's brevity over JS would probably make up for the significant whitespace difference.
I think this article is a mix of fair criticisms but at points lapses into a lot of hand waving. At points it devolves into "This is confusing/non-intuitive if you don't know CS" and "This is not the way I like to do things".
I find the proposition that porting a library (no matter the size) gives you "as much experience as anyone" pretty dubious. It's clear the author has some experience with CS, and I wouldn't discard out of hand the input of someone who had only used it for 5 minutes...but I found that particular statement a bit dismissive of people who have been using CS for a long time on a lot of different projects. Too often people confuse the "getting comfortable, I basically know whats going on here" phase of learning a language with "knowing" that language...which is really more "I understand this on an intuitive level".
I found the "We Process Images and Symbols Faster than Words" section extremely dubious and strikes me as very hand-wavy. I see what the author is going for, but it's a completely false analogy and their point is flawed to begin with.
They're trying to compare the acquisition and understanding of new concepts vs the recognition of an already known pattern, entirely different things.
Also, the author is essentially cheating, because they are talking about conveying inherently visual information using pictures. Showing someone a picture of a circle and saying "this is a circle" does not convey what a circle is, it conveys what a circle looks like (or perhaps, it does explain what a circle is, but only visually) which is to be sure useful for some purposes. However, it gives them no inherent understanding of how to properly construct a circle, or what it's relation is to other shapes (such as a line) geometrically (say compared to a triangle which could be called any three non-collinear points).
With the baby picture, again...I see what they are going for but I think it just doesn't actually support what they are trying to assert. Evoking an emotional response is not the same as passing knowledge, and it doesn't strike me as a good objective to have when writing code. Sure, show someone a picture of a baby and they will have an emotional response. However, they will have no idea who the baby is or why you are showing them the picture which would probably be more useful.
All of this does very little to support their assertion that "&&" is somehow more of a symbol and is more visually rich than "and", something I find dubious even disregarding the poor examples. Letters are symbols themselves and "&&" is just as much a "word" as any other, though admittedly more visually distinctive. With syntax highlighting, I would say that there is no objective difference between "&&" and "and".
"That is verbally readable code, but it’s not very comprehensible"
I'd say that less the fault of CS, and more the fault of the fact that the algorithm they are expressing in it doesn't make any sense:
wash plate, brush, sink for plate of dishes when plate.dirty if meal.status is 'done'
This is not a valid construct. By using "of" you are iterating through the keys of dishes, which will inevitable be strings. You later check the dirty property of the key (string) which will surely not exist. You need to either use in or less likely, access them as dishes[plate]. (I'd assume dishes in an array, in which case a loop is more idiomatic and safe than for-in)
Fine, probably an honest mistake. Sure, I just felt the need to point it out, though I think it does potentially suggest that the author is not yet at the "intuitive" level of knowing CS. You are going to have problems like this in any language where you do not yet fully know it, JavaScript included.
Even excepting that, the algorithm doesn't makes sense. You're cleaning your sink and brush after washing each plate? Doesn't make much sense. I suggest a more realistic version:
if meal.status is done
wash plate for plate in dishes when plate.dirty
wash brush, sink
The author makes a couple points about their original comprehension; how it's densely packed, and I think this largely untangles them. It puts the most important predicate first.
You don't need to know immediately that you are dealing with the dishes array... The code is composed of discrete "pieces" and each depends on the others. I think it's arbitrary to say "oh I need to know I'm iterating through dishes first thing". It's just as useful to say "ok, I'm going to be washing something, a plate...". That provides a context for everything else. The fact that it's a comprehension is pretty obvious immediately so it's just a case of deconstructing it.
So overall I find this a bit of a straw man. It doesn't make sense to begin with, it doesn't need to be so packed, and it's taken out of context which dramatically cuts back readability. If you were writing an app about doing things in the kitchen, the comprehension (even the dense version) would make quite a bit of sense because the context would make certain things more obvious. In my code, this section would probably be prefaced with a comment like:
# cleanup
wash dishes
wipe counter
stow utensils
So the fact that we just spent X number of lines getting dishes dirty, and knowing precisely what a dish object is in the context of the overall program i.e. that they need to be cleaned, it would be obvious that I need to now wash them each in turn...amongst other things.
Now really, I see what the author is going for. List comprehensions get messy fast. Sure, I just don't see this as a criticism of CS per se and I find the example poor.
Remember, just because CS offers extreme conciseness doesn't necessarily mean you have to use it. I'll admit CS users often make a big deal about conciseness, and that can send mixed messages...but I would say in most cases it's about demonstrating that power is there if you need it than saying this is always best practice. The author clearly understand this as they go on to provide more readable examples, but it bears repeating.
I could similarly break down the "one liner", but I'm running pretty long already and don't have forever. Again, what is probably a misuse of "of", and sticking more than is needed in the comprehension. I get the idea that the point is you can do this, but every language has powerful features with which to shoot yourself in your foot.
If the author actually found that in their company's code, I most respectfully suggest perhaps they don't know CS well enough or it may not be their style. At any rate, it would explain their distaste for CS.
"It’s hard to recognize instantly that I’m actually calling $.ajax"
I suppose, but CS uses juxtaposition to call functions, if you see identifier\wvalue or identifier\widentifier...it's a function call. I would say, remember CS's golden rule: when in doubt disambiguate. Feel free to add () to function calls.
I think the next point the author makes it better. I would say that it's just an unfortunate fact that in many languages some constructs will be ambiguous. I think CS does a good job of pushing ambiguity to edge/rare cases.
In the author's particular example I would note the point is a bit moot because CS has implicit return:
In other cases I would refer you to the Golden Rule.
"Now with the fat-arrow, people are encouraged to fat-arrow their way to oblivion"
I find this a dubious assertion. Stupid people maybe, but I doubt very much giving stupid people JavaScript will give you overall less problems...just different ones. At any rate, I don't see any problem with saying a tool should be reserved for experienced users.
"Therefore it will never really be supported natively, and will always be a compile-to-JS language, and will therefore always have a terrible debugging experience."
I disagree. The browsers are working on features to make such debugging much easier. Further, I know a few people working on CS in CS interpreters, so that if you need a richer debugging experience you could run your scripts through the interpreter rather than as JS. Granted it will probably never be as rich as the Developer tools in the browser, but between the two I would say it's enough.
The author noted they feel the need to throw in a lot of logging in to debug. I would say this is a style a lot of CS developers I know use, myself included. I am quite adept with the developer tools, having debugged JS since the Venkman and Visual Studio days, but I just don't feel the need for that sort of debugging very often. For me, it's either a syntax error or a problem with the flow of data through my program. The only time I really break out the debugger is debugging other people's code.
Editor macros make inserting logs dead simple. A good build process removes them except when needed. I can appreciate that some people will find this workflow annoying and a pain, which is fair. I just want to note I find it works superior for me.
So this is getting long. Despite my objections, I like this article. CS is not perfect by any stretch, and it's still relatively new. It needs work. It needs criticism from the trenches. I think most of the stuff in this article is mostly fair.
However, I find that CS suits a particular group of developers...and for them it is a very useful tool. I think that a lot of people criticize it without realizing that really they don't fit into that group.
> "I find the proposition that porting a library (no matter the size) gives you "as much experience as anyone" pretty dubious"
My intro comes off as though I'm claiming to be some sort of authority, which wasn't my intention at all. So good call on your part. I just meant "It's not like I've only spent 5 mins with the language". Lots of folks out there are dismissing it w/o giving it a shot.
However, I do know the language well at this point since it has a lot of rubyisms (and I'm alright with ruby) and at the end of the day is just JavaScript™.
Concerning images/symbols v. words. Like I said in the article, I _feel_ the relationship when I see `!==`, I don't feel it when I read `isnt`. However, I went from design -> code, so maybe imagery is a bigger deal to me.
> but I doubt very much giving stupid people JavaScript will give you overall less problems...just different ones
Yeah, one of which is not a less-than-optimal debugging experience.
> The only time I really break out the debugger is debugging other people's code.
Welcome to my article.
> Editor macros make inserting logs dead simple
I use vim, and I agree. But that's an extra few steps from my JS debugging. When something unexpected happens, I go straight the console, start adding break points, watch expressions, mutating data, etc. etc. to find the problem before I ever get back to vim. People who have always console.log'd will probably not get as frustrated as me. I feel like I'm limping.
Thanks for the criticisms. For me, the imagery is a big deal, it's not hand-waving, it's my true experience, and that of others I've talked to. We all have different minds.
Sure, likewise it's not my intention to write you off, you clearly have some good points based on real usage... I'm just trying to contrast your experience with people who have used it even longer still.
> Like I said in the article, I _feel_ the relationship when I see `!==`, I don't feel it when I read `isnt`.
Sure, I think that's fair. The point that I'm trying to make is that I think this is subjective based on your own personal background/brain/whatever other nebulous factors.
In your article it could be taken to sound like you are making a case that there is some neurological reason that "!==" is more readable, and I'm not prepared to accept that without some more substantial evidence.
I think I get where you're going, you're just trying to draw comparisons...I just want to add that those comparisons are somewhat nebulous and should not be taken absolutely.
> Welcome to my article.
I didn't get a chance to expand on that point because of space limits. What I mostly mean there is once you're in someone elses code, as far as I'm concerned it's basically all bets are off anyway. At this point when debugging other-code it's often JS, so the point is moot anyway. However, even when it's CS I don't find debugging the JS directly very difficult because it's clear enough for that purpose. Usually it's some issue like a value is not being coerced properly, or arguments being passed in the wrong order, and those are pretty easy for me to nail down with a debugger even if the source is a mess of compiled JavScript...then it's easy to fix it.
Re: debugging, I think we are on the same page. We have different styles. Yours is somewhat crippled by CoffeeScript. It may get better in the future, it may not. I just wanted to say I feel no such impediment.
> it's not hand-waving
My main objection is your examples. Like I said they could be construed as making a more scientific-objective case, rather than a more philosophical subjective one.
> This is not a valid construct. By using "of" you are iterating through the keys of dishes, which will inevitable be strings.
No, they are objects with properties like "dirty", not strings. Dishes is an object of key value pairs. I've been very active in MooTools, contributed to its source, and have written plenty of my own stuff to know the importance of iterating arrays v. objects properly, (and have since repented of extending built-ins).
While I'm on the topic, I love `for own key, val of obj`, own is so, so awesome right there.
Ok, just to be clear because this comment is ambiguous as to if you understand what I'm saying.
The comprehension in your article is not valid, you need to change one thing or the other for it to work.
> Dishes is an object of key value pairs
Yes, and you are getting the KEYS, not the VALUES. I doubt I need to say this, but per the spec KEYS are ipso facto strings.
At the point you do
plate.dirty
"plate" is a STRING, one of the key names in the dishes object.
See, here is the compiled code fresh from the compiler (with added comments):
var plate;
if (meal.status === 'done') {
for (plate in dishes) {
// plate is a STRING, plate.dirty will not exist
// you want dishes[plate].dirty
if (plate.dirty) wash(plate, brush, sink);
}
}
You're making the mistake of thinking CS will do the plate = dishes[ key ] part for you, but it won't...do it yourself or us "in" on an array.
You'll also note there are no sanity checks, so use with caution.
This is precisely what I mean when I talk about not knowing CS well enough. I mean this with all due respect and do not intend to dismiss you. You are clearly very bright and a good coder. Anyone will get tripped up in any language at first.
The point is I've used CS so much I breathe this stuff...I knew instantly that it was an invalid construct.... I'm simply saying (like any language) once you learn it well enough a lot of these sorts of issues fade into the background.
Maybe you don't think it's worth the effort to get to this point, and that would be fair enough. I just think it needs saying that some of the points you raise are related to being relatively new to CS.
Now go into the debugger, find the _ref, and then add a break point, inspect it, and you'll see.
Or if it was just JS, you wouldn't have to mess with _ref, you'd just put in a break point right where you were last looking at the code wondering "what kind of object is dishes?"
Edit: Oh snapz you got me!
for key, plate of dishes
Missed the `key`, post updated. Go ahead an chalk that up to "Ryan doesn't know CS", if you believe it's any more incriminating than missing any ol' arg in a function signature.
Iterates through the KEYS of bar in turn, assigning them to foo...NOT the values
To be more specific, it iterates through PROPERTY NAMES.
Per the JS spec, property names are always strings (you can't use objects...but you know this I'm just being thorough.)
In your example, you do
plate.dirty
At this point, plate is equal to "ryansPlate" on the first iteration, "yourPlate" on the second, etc. (well technically the order is undefined, but you get my point)
You want dishes[plate].dirty
To be absolutely clear, I'm don't want to make this out to be anything other than the sort of stupid error all of us make all the time. I'm simply pointing out that I recognize it much quicker because I have a very intuitive understanding of CS at this point. Where you, who are very good at (and used to) JS have a harder time seeing the error.
Likewise, I'm sure that you're aware of the dangers of for-in, I'm simply pointing out that it does no sanity checks so watch out.
Also to be clear, I'm talking about your example as written in the article. Maybe you have some other version that works. Go back to my other comment, I compiled your CS code with the CS compiler. You'll see the error there. If you like I'll make a JSFiddle that demonstrates the problem.
I think you've getting stuck on the fact that in my original criticism I didn't know if dishes was an Array or an Object because it wasn't clear from your code...to be clear it is wrong both ways...so I simply said "it's wrong, something must be changed" apologies if this was ambiguous. Now that you've clarified, it's still wrong...just a particular kind.
I do find it interesting that you didn't go "Oh, if he's got an object, he just missed `key`" so I could argue you likewise not as proficient as you could be :P
> Go ahead an chalk that up to "Ryan doesn't know CS", if you believe it's any more incriminating than missing any ol' arg in a function signature.
Precisely what I intend to do. Everyone makes little logic errors and whatnot, I was just trying to use this as an opportunity to show that people who are, while not necessarily new, but not super experienced...will have a harder time spotting stuff like this in the language.
This is understandably frustrating, so sometimes it colors some of their opinion.
"At points it devolves into 'This is confusing/non-intuitive if you don't know CS' and 'This is not the way I like to do things'."
Sure. Most of the arguments for CoffeeScript itself pretty much boil down to "JS is confusing/non-intuitive if you don't know JS" and "This is not the way I like to do things."
I just spent my entire day tracking down a missing "var" that caused a bug only in Internet Explorer. I haven't written any assembly code since college, and I hope to reach a day when I never have to write or maintain native Javascript again.
1. It's trendy. Snooty programmers use them.
2. It's hyped. Annoying masters of hyperbole won't shutup about it.
3. javascript is already easy to write, easy to read, easy to use, and well understood.
But really it's because I despise hype at such a deep and fundamental level; it leads people to being impulsive and emotional; making decisions based on personal reasons rather than finding the right tool fo the job.
countless times i've seen people work significantly longer and harder with the hype technology then if they had just been solid project managers and made good decisions regardless of what is being currently blogged about.
other times i've had legacy systems that were built in the super-trendy tech of yesteryear that was a fly-by-night and nobody uses any more ...
sometimes you will have managers for instance, that wlil request something like perl code to be rewritten in ruby or some other rearranging of the deck chairs kind of activity. It's a complete and utter waste of time. I'd rather go home and click on all those nsfw tags ...
so yeah, no freakin' way; unless there is native support, it's been around for a few years, and there is actual real solid evidence that it doesn't suck (extra time that is) for a similar goal.
Pythonista and Haskell hacker here. Everything he hates about Coffeescript is what I love about it, minus debugging. My debugging workflow looks entirely different (and more efficient) than his, however.
While I can't say for sure, it reminds me of the days where Java programmers would try Python and complain that it was harder to write Java code in Python than it was in Java (esp. with getters and setters).
Can you please give some details about your debugging workflow? I'd love to know what works well, as I've been a bit nervous about making the leap to CoffeeScript for more than just goofing around for this reason. I write a little Python, but I'm not great with JavaScript, and this seems like a barrier.
What are your experiences writing CS on top of another well-established javascript API framework, e.g. Dojo. I do a lot of work with ArcGIS Server's Javascript API, which is based in Dojo. I also mix in Jquery when needed (because I prefer it over Dojo). So, would CS be a benefit in these situations, or just muddy the waters?
My biggest problem is that because it lacks firm rules in a lot of area, CoffeeScript is a language that is not always intuitive. With JavaScript it's pretty simple to know the rules; they are completely uniform. Not so with CoffeeScript. Here's my biggest gripe:
That's simple. A function that takes 2 parameters, separated by a comma. But wait, here's another:
longRunning = (onsuccess, onerror) ->
dosomething (e) ->
if not e.error? onsuccess() else onerror()
And the implementation:
longRunning
() -> "Yippee!"
() -> "Oh noes"
A function with 2 parameters, this time separated by a carriage return, not commas. How would one know that intuitively? That some times functions parameters are separated by commas and some times by carriage returns? Or that some times function calls have to include the () (when they have no parameters) and some times they don't (when they do have parameters)?
I'm sure these things become second nature over time, but learning them will result in a lot of trips to StackOverflow as you wonder why your code doesn't behave as expected.
The "symbols are more comprehensible" thing is a bunch of pseudoscientific mumbo-jumbo. Not the least of all because 'and' and '&&' are both obviously symbolic, just with different symbols.
That was a brilliant article, the most complete honesty about coffeescript that actually makes sense. After reading it I really felt like coffeescript is cool. but you know what he's got something there. he really does => I'm going to stick to JavaScript ("See what I did there"). thanks;
It took me about 3 months of intense CS usage to really get the hang of it. I've written a good 20-30k lines of CS so far for my application, so I've developed a lot of good practices since I started. Now, I can't imagine writing 'pure' JS ever again.
I tend to avoid a lot of the one-line comprehensions for the reasons documented in this article (they get difficult to 'parse'). I also tend to write in a more of a Java style, mostly because of my background is in Java. I tend to put (parens) around a lot of function calls so that I can see it is still a function. Sure, CS can be abused to the point of being barely able to read, but that can be said of any language. Having standards and code-review with your co-workers kind of negates that issue.
Debugging can be a bit of a pain since you have to find the line number out of a big file. I think this will go away if/when browsers natively support CS. I've gotten good at naming things so that they are easy to search for in the developer tools. That makes finding the right line to put a breakpoint on a lot easier and faster. If need be, I'll throw a console.log('here') in there and search for that too. Context switching between CS and compiled JS takes a bit of getting used to, but it isn't really that big of a deal. I'm already context switching between Java, HTML, CSS, json, etc... what is one more?
The fat/thin arrow thing took a while to get used to, but now it makes perfect sense. The change they made (too quietly, imho) in 1.1.3 to not use __bind so much anymore really helped with comprehension. Now I can see the var _this = this in the compiled code and things make more sense to me now. It also helped performance and made me worry less about using it. In most cases, it is safe to just fat arrow things now so it really isn't that big of a deal.
In terms of workflow, I found it very important to integrate it in with my IDE so that every time I save a file, all of my CS is immediately compiled to JS. [1] As a result, I don't see any overhead with the compilation phase of things.
Being able to declare 'class'es and constructors has also been really great for compartmentalizing my code. Yes, it is possible to emulate all of this in JS, but I find that a lot more difficult to manage. I do wish there was an 'import' statement though. Right now, I hacked that together with the way I'm using LabJS.
In the end, is CS a significant improvement over JS? I think so. Just looking at the compiled output of CS, I couldn't imagine myself writing all of that JS code and not making a ton of mistakes (ie: === vs. ==, nesting of brackets, this binding, etc). People say there is a lot of boilerplate in Java... so, when I use CS, I feel that way when I look at the compiled JavaScript.
They won't, and source maps are not native support.
Check out AMD with RequireJS (make sure you optimize) for your "import" stuff. I haven't incorporated optimization yet for the project, but you can see how it works with my snackJS project http://github.com/rpflorence/snack/tree/amd-coffee/lib
IIRC I believe i read from Paul Irish (I could be mistaken) that Chrome is working to support projects such as CoffeeScript for debugging. No idea how tjry plan to implement this, but I found it to be an exciting possibility.
Thanks Ryan. I've looked at the RequireJS/AMD import stuff and it seems really over engineered for what I need. I feel like there is too much declaration going on. For now I think I'll wait to see what else people come up with along these lines.
Thanks for the pointer, I made sure to read it. What I don't like about it is having to 'define' all the modules that I'm going to use. That is just too error prone.
I'd rather just create a CS class in a file and include the entire file when I need to. More similar to the way that an import statement works in Java.
For now, as I already mentioned, I'm simulating import style functionality with LabJS, which works great. While not perfect, it allows me to async load everything I need as well as minimize the amount of data on the wire. Here is a description of what I've done...
All I can say is, I'm way more productive with CoffeeScript than I ever was with JavaScript. Debugging can be a pain. But I'm using Jasmine heavily, and finding that a steady dose of TDD eliminates most of the debugging frustration.
Some languages are designed for professionals to comprehend easily at a glance: these languages use symbols instead of words, use brackets effectively and don't rely too much on whitespace.
Other languages are designed to be easy for anyone: these languages use English as inspiration, using whitespace instead of brackets.
CoffeeScript has a place, but not in production. It belongs in the classroom, to help those wanting to learn an easy language to program in. Ruby, I believe, belongs there as well.
Languages like C, C++, PHP, JS, etc. are all designed for production. They may not be pretty to look at, but they're easy to comprehend at a glance.
It's all a question of what learning curve you're aiming for: the learners curve or the advanced curve.
Taking this argument to its logical conclusion, Perl would be the ultimate language for 'professionals' to use, since it's full of shorthand symbols. And it's true -- if you wrote the code, Perl is very easy to scan. If you didn't write the code, however, Perl is not always so easy to scan.
Which is exactly the point of Ruby, Python, Coffeescript, and other similar languages. I´ve heard it said that on average, only 20% of programming is writing original code, and the rest is maintaining other people's code. For this reason, Ruby, Python, and Coffeescript are much easier to maintain for most people than JS, C, Perl, PHP, etc.
Personally, I know both JS and Coffeescript well, and have actually done much more work in JS over the years, but the first thing I do now when I get a third-party Javascript program is convert it to Coffeescript (using js2coffee). I can read and understand Coffeescript about twice as fast as I can Javascript, even though I started working with Javascript in 1998.
++ exactly, no one gives a shit how clever or cute your code is, it should be easy to read, all the "noise" people speak of make this simpler. In fact I think that if you have a difficult time parsing it with LL it's most likely a crummy grammar, if parser has ambiguities reading it wont be much fun to read either
In addition, CoffeeScript has it's own distinct burden to bear: the entire point is to compile (in as straightforward a fashion as we can manage) into JavaScript. That you're debugging JavaScript when you run CoffeeScript in production should be no surprise -- that's the point. But it's also quite a large hurdle to jump.
What's interesting about CoffeeScript is that despite there being a strong case against using it -- and I try to make the same case in my workplace -- people still find it useful enough, and transparent enough, to use it regardless.
As of this morning, CoffeeScript is the 13th most popular language on GitHub, and the second most popular module in npm, after Underscore. All of the top ten languages on GitHub date from the mid-1990s or earlier.
https://github.com/languages/ http://search.npmjs.org/
The better question is not "What is the case against using CoffeeScript?" ... but instead: How is it that despite all of the obvious hurdles stacked against a new source-to-source language, people are actually using CoffeeScript?
That's the question I'd like to know the answer to.