Dropbox dives into CoffeeScript
tech.dropbox.com
tech.dropbox.com
Putting syntactic sugar aside, while some things are very welcome (list comprehensions, ===, ?), there are two main reasons why I would be wary of using CoffeeScript again:
1. Complete lack of documentation for syntax. Because there are basically no more braces and parentheses, CoffeeScript just tries to guess what you're doing, as far as I can tell, based on a bunch of internal heuristics. Unfortunately, there's no way for me to learn how to write parseable code without constantly pasting into the coffeescript.org site, and seeing if CoffeeScript understands it or not. This is the first language I've ever used where the syntax rules are essentially unknowable, and a lot of time gets wasted trying to discover them through trial and error.
2. Unexpected side effects. For example, functions return the last evaluated value by default. If you're using $.each(), and your function's last line is something that returns false (like a separate function you call), then your $.each() loop will terminate unexpectedly early, since jQuery does that when it receives a false. So CoffeeScript isn't just a wrapper around JavaScript, but it really changes its behavior. Another example: CoffeeScript gets rid of function hoisting. A significant JavaScript feature, completely gone.
I personally am slower to code in CoffeeScript, because I know JavaScript 100% inside and out, but with CoffeeScript that isn't really possible, because so much of its implementation is undocumented. I mean, many times you're forced to end a line with a backslash in order to continue it, and even the existence of that necessary feature isn't mentioned once in the docs.
But a lot of people seem to love the syntax, and that seems to outweigh the negatives for them. I personally don't find JavaScript that ugly, but coding in a language I can't ever fully understand gives me a huge headache.
#2 has definitely bitten me, but I see that less of a failure of CS and more of a failure of the design of $.each(). The case is pretty well documented in CS though... the last code line is 'return'ed... if your code unexpectedly returns false, then really you are just bitten by the design of $.each().
For each loops are great, but there is certainly value in having $.each be a function as well as a statement. For example, if you want to pass it to another function in more functional style programming: do_something($.each);
"or it might be possible to reduce your input first so that you are only iterating over the things you actually want to iterate over..."
It seems you don't understand the point of returning false from $.each, it is not to filter, it is to stop early. For example, if you are doing a linear search for something, it is reasonable to want to stop after you find it. There is no way to "iterate over the things you actually want to iterate over".
I also wouldn't use something called $.each to do a linear search. I'd use underscore.js _.find(), which has a sane api, you return true when you want to stop. This thing I'm looking for has been found.
Array.forEach and _.each() chose not to support returning false. I can only assume that is @jashkenas being wise.
Yes, there is a learning curve and it does things slightly differently. The implicit return value is just something you read once and then integrate. True, function hoisting is lost in cs, but that has never been a problem for me.
Mapping values in an object:
{a, b} = c
Backslash to continue lines: a = long_variable_name \
+ other_long variable name
Switch statements without a parameter: switch
when function_that_needs_to_return_true
stuff
But the bigger problem is just having no idea how things will parse. For example, this works: a = b +
c
But this doesn't: a = b
+ c
And then this works: switch a
when b, c, d
e()
But this doesn't (a multi-line form you might want to use if the variable names were really long): switch a
when b,
c,
d
e()
And this doesn't: switch a
when b
when c
when d
e()
After trial-and-error, you discover it has to be this, with a seemingly random backslashes: switch a
when b, \
c, \
d
e()
I could go on and on -- but you get the point. The whole language is only discoverable via trial and error. None of this is documented. (Apologies for taking up so much vertical space of the comments!!)2) This has to do with the unary plus operator more than just cs - since +varname is an expression, it treats it as such. Fair complaint - I've never run into that. Also, this works just fine if you put the plus on the end of the first line:
a = long_var_name +
hey_another_var
3) When would you use those?4+5) Are cases of the unary operator in 2)
6-10) Sure, this is limiting if you want to write switch statements like that.
I feel that your coding style is different, or that you're listing edge cases to be contrarian. Coffeescript has a much smaller range of syntax deviations but that's part of what allows it to be so brief and clean.
http://coffeescript.org/#destructuring
On backslashes:
https://github.com/jashkenas/coffee-script/issues/2457
Basically, if statements are that long, the code should be refactored. This makes it easier to read and reduces the opportunity for bugs. That pattern is one of the main reasons I like CoffeeScript.
switch a
when b, c, d
e()
Is clearly explained here: condition = (a) -> a in [
b,
c,
d
]
switch
when condition(a)
e()
This is a technique which is already possible in JavaScript, an example (not mine) from 2007: http://saintjohnshawn.com/2007/05/24/javascript-example-swit...The supposed "correct way" you support moves the values outside of the switch statement, which completely destroys reading comprehension of the code. It explodes the number of lines of code necessary. This isn't refactoring, it's complete code obfuscation.
It also makes the switch statement itself completely superfluous, since you might as well just do a series of if / else if's, and save an extra line of code. I mean, the whole point of switch is to pass a parameter and compare it against values.
For instance, look at the recently posted Matches.js [1] -- there are many reasons to factor out the test case when the test case is unusual. 80 character test cases are unusual.
[1] http://news.ycombinator.com/item?id=4516576
EDIT:
80 character test cases (variables or otherwise) should be factored out. The readability and purpose of the method is compromised if you have 5 groups of 30 constants. Factor the groups out into individual maintainable functions. Then there is one function per case, and an overview of the entire switch can be seen in a few rows rather than several pages.
switch (operation_code) {
case SUGGEST_ACTION:
...
break;
case PERFORM_ACTION_PREAUTHORIZE:
case PERFORM_ACTION_AUTHORIZE:
case PERFORM_ACTION_POSTAUTHORIZE:
...
break;
case DELETE_ACTION_REVERSIBLE:
case DELETE_ACTION_IRREVERSIBLE:
case DELETE_ACTION_NOT_PREAUTHORIZED:
case DELETE_ACTION_PREAUTHORIZED:
...
break;
case UNDO_ACTION:
...
break;
(5 more groups of 30 more constants...)
}
It's not variable names that push an 80 character limit. It's about translating groups of shorter constants, that taken together go over 80 lines, and which have similar prefixes, so they look very nice listed vertically, and make it very clear where their differences and similarities are.I stand by my point: telling people that, in CoffeeScript, it's proper to "refactor" this into a switch statement with no parameter and creating entire new functions, is bizarre.
Though the switch one seems surprising (read: a flaw) since it is usually safe to end a line with a comma in vanilla js.
http://inimino.org/~inimino/blog/javascript_semicolons
Basically, you can almost always remove the semicolons without trouble and more often then not problems occur from semicolons not being inserted then from them being inserted in unexpected positions. The "returning an object literal" case is the only common case where a semicolon is mischeviously inserted.
http://google-styleguide.googlecode.com/svn/trunk/javascript...
a = b
+ c
Line-breaks end statements in CoffeeScript, this is the same as a = b; +c
That should be pretty apparent. Your other examples are mostly abuses, and you'll not find "documentation on forbidden syntax" for any other language either.Javascript or Coffeescript shouldn't ever have lines ending with a backslash.
switch a
when b
, c
, d
e()For example, I use swank (https://github.com/swank-js/swank-js) to send javascript to the current page in my browser from Emacs. I couldn't live without it. I'd have to figure out how to dynamically compile it, and I'm not sure if there's any restrictions with piece-meal compilation.
It's definitely a separate community and I'd lose out on lots of good tools if I had to use it. I'm glad nobody wants to use it where I work.
// beginning of file
Coffeescript = require('coffee-script');
// compilation
Coffeescript.compile(line, {bare: true});
e.g. var f = eval(Coffeescript.compile("(x) -> x + 1", {bare: true}));
f(1) // ==> true(Genuine question, not an snarky comment)
Edit: Huh! I thought you meant "variable hoisting", which is the most evil language feature that's ever bitten me!
This is an introduction to "function hoisting": http://elegantcode.com/2011/03/24/basic-javascript-part-12-f... - Very very interesting.
If I recall correctly the main reason why this feature is not supported by CoffeeScript is because of some awkward IE bug though.
If your inner function names are sufficiently descriptive, you can write the high-level behaviour of the outer function at the top, first to be read, to get a quick overview of what it does. Then declare the inner functions below that (this also requires that the inner functions must not have surprising side-effects).
I've never seen or tried out code like that in practice, but it seems like it could work.
Not that there's anything wrong with that. But seriously, every single problem with javascript they mention is never a problem for javascript developers. You simply learn what's broken, and deal with it. Dealing with it is usually one line of code (or even less), making sure you just use ===, etc. It's really not a problem.
These kinds of posts smell a little like FUD to me, which is what I have a problem with.
I program in C++ extensively. I "deal with it" a lot. I write things like this all the time:
MapType::const_iterator it = map.find(x);
if (it != map.end()) {
// x is in map, process it
}
I've done it for years, I know how to do it, and there are great benefits to getting iterators back from my data structures. Yet, I always feel a warm fuzzy when writing data processing scripts in Python, and I get to say: if x in map:
# x is in map, process it
My point: even though one can have learned to "deal with it," there can still be a benefit to not having to deal with it anymore.The cost is not just time (although that's certainly a big cost). They rewrote working production code for fun. Why this is a bad idea will likely derail the conversation so I'll just direct you here:
The Dropbox team clearly did not do that. They translated it from one language to another; the design did not change. Much of that translation was with an automatic tool. Again, there is a difference between re-implementing something, and translating it.
Further, the examples in Joel's posts took years. This took a week. The situations are completely different.
Personally, I find it disingenuous to link to an article and say "He has my argument" without actually stating what your argument is.
See http://www.perlmonks.org/?node_id=115511 for my original opinion when I read it. Nothing I've seen since has changed that basic option. (But wow, the technologies that I thought would go somewhere. It is humbling to see how wrong I was. But the points are still good.)
Now years later consider carefully that the "failure" that Joel criticized is what lead to Mozilla, Firefox, and the second browser wars. Furthermore consider that Netscape had no real choice with its rewrite. As a company they were going to die. Hence their effort to open source the code. But they didn't have licenses to everything in their browser, therefore the open source version couldn't even run!
Now I'll grant that the decision to rewrite working production code is always to be undertaken carefully. It can easily go wrong. But in this case Dropbox decided to do it, they've done it, they've had no ill consequences (the lack of consequences is not an accident - they knew how to do it well), and they are more than satisfied with the result. Given those facts it would probably be a good idea for you to be taking notes and asking yourself how come your immediate prediction turned out wrong instead of saying, "They were stupid to try that."
Netscape was already fucked prior to starting the rewrite, mostly due to their ill-conceived forays into "groupware".
If Navigator wasn't rewritten when it was the layout engine from the rewrite wouldn't have existed to power Firefox and Mozilla probably wouldn't exist as an entity right now. So in a very indirect way, the Netscape rewrite actually saved the Netscape browser lineage and probably helped the web from falling into a much longer mini-dark-ages than it did around the NS/IE 5-6 era. And there's not really any good evidence that it did the old "Netscape" any harm since it isn't like Navigator was ever a significant profit center anyway (I know they did charge for corporate usage of Navigator, but that was never going to be a sustainable business, so the rewrite had no impact on that).
Quick example of how you're wrong: their version history is now trashed. They're going to pay the price for that for years.
if (map.find(x) != map.end()) {
}if (map.find(x) != map.end()) { // do stuff with x }
You don't need the iterator to do anything with x. And if you do need the iterator (to perhaps keep iterating or something?) then you would also need to do extra work in the JavaScript example.
Every hour spent converting JavaScript to CoffeeScript code, could have been spent improving Dropbox.
Doing the math of salary per hour that each Dropbox coder involved in such conversion earns, what is the cost of converting the code to CoffeeScript?
There are almost certainly other projects they could have worked on for that week that would have resulted in a more tangible business gain for Dropbox as a company, but the point of teamwide hackweeks/hackathons/etc is more employee morale than anything else. It seems to me that it succeeded pretty well at that goal.
What new bugs were introduced by the rewrite? Did they open the door to any XSS attacks? Who knows.
I'd be comfortable that no new attack vectors were opened.
Porting JS code to CoffeeScript is not very hard to do safely. I've done it myself for fairly large pieces of code (not this big.) In this case, it was for a hack week project and was a modest amount of code. It sounds like they got buy-in from the rest of the team that using CoffeeScript is a net win. It sounds like they are moving forward, it was a good decision and use of time, and they are happy with the result. Why do you think you can make arguments that it is in fact not any of these things, when the people who did it are stating otherwise, and the organization is acting otherwise? Why do you think you can make arguments against re-writes that suggest avoiding them because they never ship, when this project has, in fact, shipped, and did so in a week? Either you are missing something, or Dropbox is deluding themselves and are just falling for hype. What do you think is more likely?
That's not to say that you can't lose a lot of money refactoring working code — I dislike rewrites, they always cause unforeseen problems. But great software isn't a function of how many hours you've put into it. It's a complex result of developer morale, time, energy, experience, and skills, and there's a huge benefit to "hey I can think in a language that makes sense to me, instead of remembering weird bugs I have to work around." It's thinking on a higher plane. It lets you more easily load the program into your head. [1] And it can be all the difference if you want a truly exceptional product. [2]
The authors' arguments were correct and showed an understanding of their tools' histories.
That's not a fair statement. I'm a competent js developer, having trudged up that curve, and I still love cs. Not for lack of learning, but because I value brevity and reduced keystrokes.
Second, I'm going to disagree with you on having a polygot codebase. If the developers decided that CoffeeScript is a better environment than Javascript (which you may disagree with), then I think it's clear the ideal scenario for them is their entire codebase is in a single language. Since it was tractable for them to convert the existing code, it seems logical to do that (not withstanding concerns like version control history, etc, which seems like an academic concern not really that important in practice.)
In fact, converting an existing code base is a common way to learn CoffeeScript and come to a judgement if it is suitable for future development. Such seems to have been the case here.
Just deal with it and move on. It's really not worth trying to achieve some sort of "Wow I love this language the best it's brilliant" state. It's far better to achieve a "I'm creating some cool stuff that solves a problem" state.
Language syntax is simply not very important. Every single language has quirks. Learn them, and move on.
If you're unhappy working in a particular language, maybe you're not cut out to be a software developer. You should be happy you're creating things that solve problems - if you're not, reconsider your career choice.
You see it in everything in life though. The best way to become a fantastic cyclist say, isn't to buy the best bike out there. It's to buy a crappy cheap bike, and learn how to get the most out of it.
Which makes me realize I have no idea who the world's best programmer is.
For example, obsessing over the use of a semi-colon to signify the end of a statement in your language syntax.
In other words, why would anyone use Emacs?
serious lol
["dog","cat"].forEach(function(animal){ console.log(animal) });
Which will run just fine on your development browser, but will break for real customers.
FYI, I don't use coffeescript, but I am at least aware of some problems with Javascript.
[1, 2].forEach (i) => alert i
Everything else, like being easier on the eyes, having built-in classes, not having to spell "function" a million times, etc, is a welcome bonus.
And that is true. Nothing is exactly wrong with JS. In fact many things are great about JS, which is how we end up with ClojureScript and CoffeeScript and x86 emulators. JS is very powerful -- maybe too powerful. There are a million ways to accomplish the same task, which means that during an initial look at a new JS codebase, the program flow is not obvious.
Some developers hate semicolons, some always put commas at the end of the line and some at the beginning, some always use braces and some don't use braces with single-line statements. One can never be sure where a statement ends, or what constitutes a code block, unless one hunts down all the trailing braces, parentheses, and semicolons. I'm not even going to mention all the possible inheritance patterns.
CoffeeScript is not nearly as flexible in syntax. There is one way to make a code block, one inheritance method. It doesn't need semicolons, braces, or parentheses because of its strict style. Therefore there is less to think about. There is less to go wrong. No one needs to use a CoffeeScriptLint, because CoffeeScript is not full of non-obvious 'gotchas'.
CoffeeScript code is more concise and more intuitive, because it reads more like an English sentence. When I read another developer's CoffeeScript I can begin understanding the meaning instantly, rather than having to spend time determining the structure and programming technique. List comprehensions, ranges, and other functional programming tools simply don't exist in pure JavaScript -- the JS equivalent is much more verbose and much less obvious. CoffeeScript encourages creating small flexible code blocks, which together make a powerful structure. This all leads to fewer opportunities for bugs to appear.
The only thing I don't like about CoffeeScript, is that I didn't keep an open mind and start using it sooner. I think anyone who considers themselves a serious JS developer should give CoffeeScript a try. It's a much more relaxing, simple way to write powerful JS.
(Credit goes to Spine.js switching to CoffeeScript to finally push me into it.)
EDIT: Of course it is possible to write ambiguous, unclear code in any language. CoffeeScript is an opportunity to let go of old habits and find a fresh perspective on problem-solving: Maybe there is a better way to do things. (I had to let go of semicolons!)
CoffeeScript's functional programming style has changed the way I approach programming. My code is now more robust and less complicated. Less bugs and less keystrokes mean I ship faster. CoffeeScript is a better language because it's a tool which makes me a better developer.
There's certainly many different ways to do things in CoffeeScript, like even calling functions (parenthesis is optional). Talk about having to think about syntax! http://stackoverflow.com/questions/12077114/coffeescript-syn...
There are other ambiguities too due to the arrow pattern for defining functions. You have to be careful where you place stuff.
CoffeeScript is definitely an improvement in some ways, but please don't market it as superior to javascript (I certainly prefer brackets). It's just different. All of this really doesn't make much difference in how long it takes you to ship products.
Personally I find single-line statements that do too much to be difficult to read.
I also find nested functions really difficult to read in CS. From a coffee mug I own:
coffee.height drink coffee, e.angle
That's only 2 functions, it gets much more confusing when it's 3 or 4 which multiple parameters. Before you say "don't nest functions that way", I agree completely, however a large part of the appeal of CS is reducing LOC and these one-liners, so in real world code I've come across this often. coffee.height(drink(coffee, e.angle)) (coffee.height (drink coffee, e.angle)) flip coffee.height e.angle $ drink coffee
Where "flip" is the function that reverses the order of two arguments of a function and "$" is basically like a set of parentheses that are implicitly closed at the end of the line.So, you should write than as
coffee.height (drink coffee, e.angle)
Also, understand that you can use parenthesis in a lot of places to remove ambiguity: setTimeout (
# ...
), 400
instead of setTimeout
# ...
, 400
Or doSomethingWithHashAndOtherVars {
var1: 1
var2: 1
var3: 1
}, var4, var5
instead of doSomethingWithHashAndOtherVars
var1: 1
var2: 1
var3: 1
, var4, var5
-------Rule of thumb: When in doubt (or fear of future doubt), throw a couple parens around expressions, and a couple braces around objects.
Number of lines of code has been shown to be one of the few good indicators for predicting defects. CoffeeScript significantly reduces the number of lines of code in a project and therefore the number of defects.
CoffeeScript is cleaner and easier to read, which is better software engineering. CoffeeScript solves many of the issues with JavaScript. The fewer warts and plumbing people have to deal with related to the language, the more they can focus on the actual problems they wanted to solve.
This is a little bit like the architectural decision to program user applications in languages and platforms with garbage collection and built-in frameworks rather than using a language like C or C++ with manual memory management and minimal standard libraries. Sure you get less control over the lower-level details such as allocation and deallocation of memory, but those lower level details actually are distracting from the application domain and were actually repetitive and could be automated. So the best way to solve those problems is to move to a platform that abstracts them away or handles them for you.
The reason that developers don't like CoffeeScript (other than the fact that they haven't learned it yet, and don't want to take the time) is simple, psychological rather than logical, and its the same reason that they don't like platforms like the ones I was talking about above: it makes things significantly easier, and that is problematic psychologically for programmers for two reasons:
1) The whole point of being a programmer is that you are mastering complex ASCII codes that other people can't decipher. Making things less complex takes away part of your identity as a programmer (not for everyone obviously, but for many).
2) Many programmers instinctively and falsely believe that any system which makes their job significantly easier must automatically come with compensatory trade-offs in things like performance or flexibility. This is intuitive, but false.
CoffeeScript improvements: indenting two spaces and using -> plus no closing parens and curly braces makes nested callbacks much easier to read and write, fat arrow binds this making it easier to use classes, class and extends provide a standard, simple way to create and extend classes, iterating over arrays and objects is much easier and cleaner, control flow and function definition is cleaner, better variable scoping, simplified closer wrapper in loops, more straightforward boolean operators, existential operator for cleanly and simply checking for variable existence.
Thought of one other explanation for not liking CoffeeScript: plain old status quo bias. http://en.wikipedia.org/wiki/Status_quo_bias
Number of SLOC corresponding to defect count is not something you dispute is it?
Is there a measurement term for number of symbols (in the lisp sense) in a given program? Example: '+', 'add', 'defn' would all be individual symbols. I could see a loose correlation between symbol count and defect count... maybe.
Or to go even more abstract: number of concepts per program is probably the ultimate measurement we are looking for: https://en.wikipedia.org/wiki/Concept
But CS doesn't encourage cramming more code in a single line, like veteran C++ers do.
It just give you nice shortcuts for what you have to manually write in vanilla JS (like maps, array comprehensions, ...)
Curious - is there evidence that that applies in a cross-language manner? It seems intuitively sensible that a smaller project will have fewer bugs than a big project, but that's not the same as saying that a big project rewritten in a language which causes fewer lines of code to be used will result in fewer bugs - in the latter case, the systemic complexity is the same.
From a random Google search, http://panko.shidler.hawaii.edu/HumanErr/ would seem like a promising online resource to try to find some of the literature on error rates.
The token reduction is not that significant and the OP is honest enough to note that "line" reductions boil down to lack of closing braces. Relevant study to cite would be one that compared languages with or without explicit notation of scope. Intuitively it seems to be that braces are a visual aid that reduce bugs and do not promot them.
Do yourself a favor and spend a day with CoffeeScript and then tell me what you think intuitively.
CS's scoping is a bit... dangerous, because there's no indication of a variable's scope. If you're not paying attention to your environment, you'll inadvertently stomp on higher scopes or globals. You can lose assignments through typos too.
JS made a mistake by allowing globals by omitting "var", but that at least can be worked around by using a linter or "use strict". A number of languages have made this mistake, and many later followed a similar path to fix it.
CS has a lot of nice properties, but we should not laud conflating variable definition and assignment. It has always been a mistake, and I hope CS's author eventually comes around and fixes it.
Always open to debate though, and note that other dialects (Coco, perhaps LiveScript) have added another operator to distinguish higher scoped assignments.
Not just that, but adding a variable to an outer scope can break functions that were previously just fine. Isn't the whole point of scoping that changing code in one place won't affect unrelated code in another place?
That being said, some of their examples are lackluster.
@originalStyle = {}
for k in ['top', 'left', 'width', 'height']
@originalStyle[k] = @element.style[k]
Should really be something like: @originalStyle = ['top', 'left', 'width', 'height'].reduce (hash, position) ->
hash[position] = @element.style[position]
hash
, {}
... though that shows off some CoffeeScript warts.Also,
Sharing =
init: (sf_info) ->
for list in [sf_info.current, sf_info.past]
for info in list
@_decode_sort_key info
Why aren't they using CoffeeScript classes? class Sharing
constructor: (sfInfo) ->
... @originalStyle = _(@element.style).pick ['top', 'left', 'width', 'height'] @originalStyle = {k : @element.style[k] for k in ['top', 'left', 'width', 'height'}
Unfortunately this syntax was proposed and rejected [1]. But with a simple to_hash library function, you can achieve something similar [2]: to_hash = (pairs) ->
hash = {}
hash[key] = value for [key, value] in pairs
hash
@originalStyle = to_hash ([k, @element.style[k]]
for k in ['top', 'left', 'width', 'height'])
[1] https://github.com/jashkenas/coffee-script/issues/467By doing it this way, you've introduced another symbol for us to consider (hash) and by departing from the standard for loop format, you've made it less idiomatic, which means more people will have to expend mental energy parsing it.
original_style = element.style.only('top', 'left', 'width', 'height')
Or if you do not want to rely on ActiveSupport: original_style = Hash[%w(top left width height).map{ |p| [p, element.style[p]] }] %w(top left width height).reduce({}) { |memo, pos| memo[position] = @element.style[position] and memo }Why not convert all your Java code to Scala? Because you have real work to do and reskinning your existing code provides no benefits, that's why. If it works, don't rewrite it, go make more stuff work.
Maybe the dropbox folks don't have anything else to do, though. Not my place to judge.
For a related sample, with obj-c I move to ARC ASAP, and plan to introduce the syntax literals as soon as possible. I certainly move the whole app codebase to python if exist a cheap way -and still 100% interface compatible to the coccoa-api - as like move js->coffescript for sure!
Is masochist persist in a old way just because is harder. Like when some refuse to upgrade the same language codebase to improve things (for example, I will move to LINQ, Lambdas, parallel code, async in C# as I move along) IF the change provide some advantage - reducing lines, better API, more performance, better clarity, etc -
I tough constant -but careful- refactoring of a live project is very healthy.
Is better doing it when the project is moving, that wait until a major event happend in the industry -like what happend to my competitors, still on .NET 1 / VB Classic- and suddenly, your move is damm hard, too costly, and you have no skills in the "new thing".
I certainly think something that replace or massively fix JS (including, a better JS) will be very usefull.
If is ok to move from inline styles to CSS, and from HTML to HTML5, then move from JS with issues related from years of hacking-along-design to anything else, like CS, is a good effort.
And as everybody has been repeatedly pointing out, switching from JavaScript to Coffeescript isn't what you're describing.
Tools? The tools aren't even close to what's available for JS.
Libraries? Libraries aren't even relevant since Coffeescript can call out to JS and vice-versa.
Translating to Coffeescript sacrifices tooling and a codebase you know for a prettier syntax and reliance on the much smaller, much more insular community driving the language. And I'll say it: syntax is a stupid reason to abandon code you KNOW for code you're about to write. A stupid, stupid reason. Engineers need to be more mature than liking to type fewer characters for an anonymous function or because list comprehensions are 3 lines fewer than a for loop.
By rewriting in coffeescript, their version history is likely trashed. They need to relearn the entire codebase. They need to develop new tools to replace the old ones they relied on, or more likely, will just live without those tools.
For a prettier syntax and a couple fewer gotchas. And if a team of 3 can't avoid the common JS gotchas in their day-to-day, I certainly won't trust them to rewrite an entire codebase in a language with different semantics just to get better syntax for what already works.
CoffeeScript is just "clean" JavaScript. You can (and I have done) convert a 200 LOC JS code to CS in less than 15-20 minutes and think carefully about every change you make.
Java -> Scala is like re-architecturing your project. JS -> CS is more like re-factoring.
mtime works fine until you want to add other steps that are dependent on the mtime of ephemeral files occuring previously in your process. Steps such as minification, uglification and comparison for uploading to a CDN all become a bit more complex if mtime is used since every compile step has the tendency to modify mtime so recompilations cascade through the process causing at best unnecessary processing and at worst weird edge cases if the order of middleware isn't taken into account (the middleware issues can be avoided with precompilation before production.
Maybe it's a matter of training, but
@_update_status_position(e, files)
looks much clearer to me than @_update_status_position e, files (@_update_status_position e, files)
among CoffeeScript coders whenever there's a need for disambiguation. Also, it really depends. I bet that print statements don't look weird to you even though in most languages they don't use parens. some_function arg_a
And then later on, decide it doesn't need an argument after all, and simply delete it: some_function
And then discover it doesn't work, because you have to remember to add the parentheses back in: some_function()
It's the little inconsistencies like that which keep driving you nuts... :)Remembering to call a function differently depending on whether it has arguments or not has been a common source of errors for me.
It's also much easier for me to parse the difference between "foo(bar(baz))" and "foo(bar, baz)" than it is for me to parse the difference between "foo bar baz" and "foo bar, baz"
Update the status position with e and files.
Update the status position, e. Files.
It parses better with single argument functions (do_something arg), but as soon as you throw that comma in there, things get confusing. $ ->
# do something on document ready
$.ajax('path', options, (data) ->
# do something with the results
Instead of $(->
# do something on document ready
$.ajax('path', options, (data) ->
# do something with the data
)
)
Those trailing parens are ugly, and they're very common if you do event-oriented javascript via closures. $.ajax('path', options) (data) ->
# do something with the data
But it does read less clearly, unfortunately.I've heard this quote from Eich several times now, but I've never heard what the "worse than JS" alternative would have been. Anyone know?
(http://www.jwz.org/blog/2010/10/every-day-i-learn-something-...)
"If it can be moved to the line above, without any other changes, it's not its own line of code."
The argument that CoffeeScript saves countless }); }); } } is invalid, IMO. I would even argue that function definitions are not lines of code. Example: (ruby)
def foo end
0 lines of code.
BTW, I'm not a CoffeeScript hater, just trying to level the playing field.
I agree that counting }); as a line isn't necessary, but CoffeeScript saving you doing any of that is a very valid benefit. It cleans up code dramatically- ignore line numbers and just think about all the extra stuff that ends up surrounding multiple function calls.
Have you tried CS? It's far more readable, by virtue of not having "function() {}" splattered all over the page. I don't care/worry about keystroke reductions, I care about readable code. The lack of curly brackets and addition of significant whitespace absolutely does make for more readable code.
My code already has significant whitespace; it's required by styleguide and linting. CS has prolific issues with whitespace usage, especially when it comes to parsing function arguments and lines broken over multiple lines. Just look through this or any other thread on CS being pushed.
CS often has mind fucks, and I'd rather have to ignore a couple lines with } on them.
Note: I mostly write in Python, if that gives any indication into how I very much enjoy significant whitespace.
I have personally had more problems from not closing something than I ever had from assigning a variable.
CoffeeScript agrees.
Under this logic, all Javascript files are 1 line of code.
>0 lines of code.
I'm having trouble understanding your point here, how exactly is that 0 lines of code when you've actually written 1 line of code which directly changed the method definitions of a class?
...then I hope that I never have to work with code you've written, because that certainly is given its own line for readability quite often.
What use is a LoC comparison that doesn't use conventional/reasonable formatting? Technically I can run a program of any length through jsmin and come out with one "line of code", but how is that at all useful?
The other argument is that }); is line noise and therefore unnecessary, which I think boils down to preference.
JavaScript:
this.originalStyle = {};
['top', 'left', 'width', 'height'].each(function (k) {
this.originalStyle[k] = this.element.style[k];
}.bind(this));
Coffee-script: @originalStyle = {}
for k in ['top', 'left', 'width', 'height']
@originalStyle[k] = @element.style[k]
coco: @originalStyle = @element.style{'top', 'left', 'width', 'height'}
coco also solves a rather nasty variable scoping issue[3][1]: https://github.com/satyr/coco/ [2]: http://satyr.github.com/cup/#c:@originalStyle%20=%20@element... [3]: https://github.com/jashkenas/coffee-script/issues/712
Did any of the other Dropbox employees have a hard lead-in time getting used to the syntax in CoffeeScript? Would you say you run into a lot fewer syntax errors when running validation now?
Obviously CoffeeScript is less cluttered, but JS is nowhere near as bad as the audio makes it sound.
At any given time, I'm likely to have at least two of the following with me: a phone, a tablet or a laptop. Just e-mail me the code. Make a gist with the problematic section. Push it to a branch in our git repo so I can take a look at it myself. IM it to me. Don't read it to me.
what does new-language let your team accomplish that you can't accomplish with old-language?
question isn't answered in post, the answer is probably somewhere along a 2x increase in the complexity the same team can handle. i dunno if it is worth disturbing an existing codebase and the QA cost of making sure you didn't introduce any defects. meh. where i work, we have maybe 100k lines of javascript, and its not really a problem other than lack of types when refactoring imperative code, nothing near the scope of the problem of Java's lack of higher order functions.
Coffee Script is clearer, more concise, more maintainable. It has made me 5x more efficient in writing code (just the reduction in scrolling is a massive time savings) and I've seen similar results with my developers.
It takes about 2 days to learn and has a significant long term benefit. Switch.
I wonder how did they achieve this? Is this a feature of node.js or is this like an automated build system? I suspect the latter.
Is there something I don't know about?
oh wait, no semicolons :/
> Disclaimer: we love Python, and it’s Dropbox’s primary language, so we’re probably biased.
Unlike CoffeeScript, but like Python, it has proper list comprehensions `["#x#y" for x in [\a \b] for y in [1 2]]` which I think is very important to any Python fan, dict comprehensions, and (more minor) you can use `is not`, like in Python, instead of `isnt`. (Along with many other features.)
So, in other words, you have only your own opinion and at best one (1) anecdotal experience. Congratulations! You've failed to prove your point from a logical perspective, let alone anything more!