This essay is fairly amazing -- there's so much emotion and so many assertions packed into it, and yet they're all founded on innuendo and hearsay, not on how things actually are. Most of the things Leon writes are factually incorrect, but at the same time, you can see how listening to enough FUD might make you start to think them...
For example:
> CoffeeScript classes have attributes and properties that
> are encouraged to be mutated by reference so a language
> contrivance was devised (@) to allow OO style encapsulation
> while still trying to provide Functional referential
> transparency in anonymous contexts… wrap your head around that.
What? Instances of CoffeeScript classes are just like any other JavaScript object with a prototype -- just like any old string or function or array. The "language contrivance" `@` symbol is simply an alias for `this`. Nothing more and nothing less. `@name` in CoffeeScript is just `this.name` in JavaScript -- I'm not sure how you wind up with "functional referential transparency in anonymous contexts" from there ... but I'll have a little of whatever Leon's smoking ;)
There's many other things in the essay, but the core boils down to this: There are a bunch of folks who are used to ignoring prototypes in JavaScript because they hand-roll objects by manually adding properties or using Crockford's "module pattern". For them, CoffeeScript's `class` keyword feels like it's adding something alien to JavaScript because it makes prototypes accessible. It's not. Using prototypes and objects lies at the core of JavaScript, and it's only the difficult and fragile syntax that leads people to manufacture new instances like this:
Finally, CoffeeScript isn't encouraging you to do anything in particular. If you truly don't like using prototypes in JS, feel free not to use `class` in CS.
> Jeremy Ashkenas, whom I do not know but would
> LOVE to have a drink with (alcoholic drink or
> otherwise) and pick his brain ...
Ping me the next time you're in NYC. Always happy to grab a drink and talk shop.
Did HN's "no editorializing the original title" policy change, or does it go absent on Fridays? How did "My Perception of CoffeeScript" get changed to "Finally, an objective and balanced look at Coffeescript vs Javascript"?
This post doesn't make much sense to me either. JavaScript encourages an object-oriented style, but with prototype-inheritance and flexible method binding (where `this` in a function is determined by the caller). CoffeeScript's features embrace both of these design choices and make them easier to use. Using the `class` keyword to set up a prototype chain is really nice, and `@` opens up whole new possibilities that would have been too syntactically heavy with the `this` keyword.
JS is object-oriented. If you really want to write functional code in JS, you're going to need persistent data structures, like Clojure (and presumably ClojureScript) provide. Otherwise you'll pay a high cost for constantly copying objects. Without persistent data structures you pretty much need to mutate properties to remain performant. And if you're mutating properties, object-oriented code is a good approach.
I don't understand why the author sees randomly assigning functions to anonymous objects as a superior approach to using prototypes.
Nice to have you chime in here Jeremy. I personally use CoffeeScript a lot. I also code in Ruby and understand its appeal from that perspective. However I am curious what your take is on dxbydt's reply below. He makes a very interesting comment about the overhead of objects vs simple message passing/currying/etc.
My take is that it's a classic straw man -- you can write inefficient functional code that constantly manufactures and throws away closures ... just as easily as you can write inefficient object-oriented code that constantly manufactures and throws away objects.
The point of object-orientation is that "simple message passing/currying/etc" rarely stays simple for long. Eventually your system wants to have many different pieces of data that all share common functionality, and objects are a great way to model that. But if you prefer to work procedurally, and have a big data structure and a bunch of little functions that operate on it -- that's fine too. CoffeeScript and JavaScript support both.
> "you can write inefficient functional code that constantly manufactures and throws away closures ... just as easily as you can write inefficient object-oriented code that constantly manufactures and throws away objects."
Very true, except that it doesn't happen in practice.
There's a more elaborate discussion in Joshua Suereth's book "Scala in Depth" on this very topic, because Scala has the exact same problem. A boatload of OO Java guys are now migrating to Scala, bringing their OO with them. Teaching them Scala is trivial, teaching them FP is not.
The example Joshua gives is
"a cat catches a bird and eats it".
How should programmers model that ?
What programmers should do is focus on the verbs. So first define a catch function and an eat function. Then compose them like this: catch(...) andThen eat(...). Define a Cat trait and a Rat trait & you are in business.
What most programmers will do is focus on the nouns. So they will first define a class Cat {} with two methods catch() and eat(), then a class Bird {}, then wonder about whether there must be some class Animal {} which Cat & Bird must inherit from, except that birds aren't really animals, so...finally they'll create a cat object and a bird object and call cat.catch(bird) and then cat.eat(bird). When the program runs, there'll be many cats & many more birds & all these little objects will unnecessarily bring things to a crawl.
This has nothing to do with CoffeeScript the language, which ofcourse supports FP as much as it does OO. Its just that in practice people latch on to the OO aspects of CoffeeScript because that's just how most people process problems.
I note the same in another part of this thread, showing how you can write his JS in less CS code.
However, I'd argue that the reason most people are adopting CoffeeScript is for the "class ... extends" as it lets them model their JavaScript code much like they might have in their old Java, and more likely, PHP projects. And once you throw that out, the main draws remaining are default argument values, string interpolation, and the ? operator.
The question is, do those benefits warrant the added layer of complexity? Many of us who have worked with JavaScript for a very long time wonder about this, and dread the deeply nested class hierarchies we might encounter in CS projects because CS makes that so _easy_ to do. But bad programmers will write bad code in any language, and I hate to think what a dev who uses `class ... extends` as a crutch would write in plain JavaScript. So, I have both positive and indifferent feelings about CoffeeScript, the only negatives being with the handling of variable scope/shadowing, which is of course opinionated and neither right nor wrong, and the parser itself which makes for ugliness like
window.addEventListener 'load', ->
# Do stuff ...
, false
# leading comma is needed above.
the only reason i use CS is because I can get away with typing way less than needed. There is a one time cost (huge? or not!) for setting up tooling to compile it automatically.
If not anything, typing is the only reason I love CS.
Because studies have shown (anyone got links, i know them from greg wilson's talk from CUSEC)
1) Less lines of code - less mistakes
2) You produce same number of lines of code, no matter the language
and (not related to studies)
3) less lines to debug
4) if you can deal with the idiosyncrasies of JS, that person can deal with CS quite well. (stawman argument anyone?)
> If you’re a fan of OO you’re going to love CoffeeScript from day one, guaranteed. If you’ve never tried to see the world through Functional glasses then I assert that you’re going to be missing out on a few beautiful vistas.
I feel like he's not using the same CoffeeScript I am. Yes, it does make OO-style programming easier than vanilla JS, but it also makes functional programming a heck of a lot more accessible. The terser function creation syntax and the 'everything is an expression' mindset might appear at first glance to be minor improvements, but they add up to a language where working in a functional paradigm is a lot more enjoyable than regular JS.
I have to agree with you here. Since using CS my code got far more functional because I could create functions that create functions really easy and the implicit returns make it feel like a functional language.
Not to mention it's based on underscore and has almost all the Lisp functions baked in. That coupled with less verbose function syntax and it's an FP programmers wet dream :)
This may sound pedantic, but I wish that these type of technical screeds were created with the flexibility/power of online publishing in mind.
There needs to be more thought given to the use of non-prose in communicating these things. More tables, a la Ed Tufte's principle of direct comparison and small multiples, is a huge help. At the very least, it helps break up dense text and forces the author to be more aware of how to best not bury important statements and assertions.
But if you aren't going to go all-in and use tables/graphics because it's a bit of work...then at least use headings, bulleted lists, formatted-text...this is the web, after all.
Or, if you don't even want to do that, at least use line breaks like everyone learns about.
And if you plan on including code, please use a wide enough format (and of course, syntax highlighting, though that is additional overhead).
I don't mean to belittle this author's point on the basis of visual cues alone. It's just that I see a lot of great essays that could be so much more influential and effective with just some additional effort put into presentation.
these are all features you can blithely ignore: don't use backticks, and stick to native JS prototypes and the thin arrow.
Without taking a personal stance on these features, I suggest that “You can ignore Language Feature X” is often a weak argument. If you feel X is harmful, not only must you ignore it, but you must consider all of the other developers, maintainers, and so forth who interact with your code.
You personally may not use X, but what about your team mates?
Your team (of 1+) may not use X, but do you ever have to debug modules or plugins written with X?
If you really think X is a terrible idea, it’s in your best interests to discourage its existence, not just ignore it :-)
Without taking a personal stance on these features, I suggest that “You can ignore Language Feature X” is often a weak argument. If you feel X is harmful, not only must you ignore it, but you must consider all of the other developers, maintainers, and so forth who interact with your code.
There are tools that can cover this.
You personally may not use X, but what about your team mates?
Team leads should tell team members to use automated tools for coding standards. This takes care of most of this.
Your team (of 1+) may not use X, but do you ever have to debug modules or plugins written with X?
The Node.js community has a way of dealing with non evented-friendly code. Programming as a whole would probably benefit from more things like this.
I think we’re making the same point, namely that if “X” is harmful, simply ignoring X is insufficient, you must take active measures to avoid being “contaminated” by X.
We can debate how much effort is involved firewalling your code from X. It obviously varies depending on what precisely X is and how popular X is, but you clearly have to do more than simply “ignore” it.
We can debate how much effort is involved firewalling your code from X.
Well, this thread has brought me to the awareness that there is always going to be some effort in this regard. Whichever programming language is used makes no difference.
FWIW, I agree about tools. I’ve certainly experimented with integrating Lint-like tools into the build process. Every team ought to have coding standards they enforce, whether mechanically, by eye, or a combination of both.
Pycharm and related IDEs have Lint style tools for Javascript, for example. If your needs aren't met by existing tools, you can also roll your own. Many Lint-style programs have ways of coding your own checks.
Frankly his main gripe is that JS = FP, CoffeeScript = OO.
The 3 gripes you point out are mostly a consequence of Coffee choosing to do OO instead of FP & then needing hooks into JS's world to achieve this end. From the article - "CoffeeScript sees the world through OO’s eyes. When I see JavaScript, I find beauty in it’s ability to be a dynamically typed Functional programming language."
He wants just 2 "objects" ( he more specifically wants unstructured objects, or plain structs ) - sammy the python & tommy the horse. Functional JS gives him just those 2. But OO CoffeeScript gives you 5, or even 6 if you count hasProp.( var Animal, Horse, Snake, sam, tom, __hasProp. )
The more dramatic way to document this behavior is to have say 100 cats & 500 rats, whoe identity is captured in an int. With FP you'll be able to happily get away with just 2 "objects" - 1 cat and 1 rat ! The int indexing will happen via a function call. With OO you will have 600 little objects plus a bunch more for the classes and the hasProp !!
Here's another one I've seen - If you get two programmers to write a paddleball - one in functional JS, and the other using Coffee OO. The Coffee one will lag pretty soon. The Coffee guy will have a Ball object & a Brick object & a Wall object & each gesture will get captured as an object as well. So when the player moves the arrow keys to move the paddle, each move's x,y coordinates will become a gesture object & then gestures are passed to the ball & the wall to determine collisions....soon you'll have 1000s of gestures & the pgm will slow down unpredictably as the gc kicks in.
The functional JS guy won't create a single gesture object, he'll simply call some collision detection function directly with the x & y, so you'll see much better performance.
Ofcourse the coffee OO code will look a lot prettier than the FP one littered with curlies & function calls, but like the author says, "JavaScript’s function keyword and curly brackets aren’t ugly to me, they are useful indicators of code smells."
I am not a fan of CoffeeScript for my own reasons (it doesn't offer enough for me to drop drop JS), but there is nothing stopping the author from writing his CS like so:
move = (aDistanceOf) ->
it = @name or "It"
alert arguments[1] if arguments[1]
"#{it} moved #{aDistanceOf} meters"
snake =
name: "Sammy"
horse =
name: "Tommy"
move.call snake, 5, "Slitering"
move.call horse, 45, "Galloping"
But really, this is just JS wearing some new clothes. The string interpolation is nice, but apart from that, you really gain nothing over the JS version.
Dude. Really ? You've just invented objects, but you'd rather not call it that. You have two attributes ( name & distance ) and you've made an attribute out of the method ( move_verb ?! heh heh ) as well, very clever. You are then calling a function & asking it to sort it all out. If we are going to call this FP, that's a real stretch. What objects buy you is that name,distance & move_verb are common to both snake & horse, so they should be refactored to some base class & then snake & horse should be instances of that class. But then you won't do OO, so you must encapsulate in this roundabout fashion:) I'll grant it does give you FP + encapsulation.
shiffern's example uses the same coding style you use in Clojure. It doesn't feel like stretch to call it FP. Start with a basic data structure (the object in shiffern's example would translate to a map in Clojure) and then operate on it with simple functions.
The example given by author is very FP and not OOP, since the move function doesn't modify shared state and the only side-effect is output.
We use CoffeeScript for node.js and browser client code. I think classes more useful for control patterns, like EventEmitter in node.js, and much less for wrapping of data, as done in traditional Java/C++ -style OOP or ORM models.
I'll chip in Erlang (real production-grade code with type-specs, not a short REPL example):
Well stated, and I agree 100% it is a sort of blindspot for this article. That said I agree with him on a level that CS does encourage a sort of OO band-aid approach towards functional programming and will definitely impede newcomers from embracing the power that comes with the built-in paradigm. I feel that it should not be in core, but am happy as you are to simply use what works and ignore what doesnt
^ Thanks for sharing that. I hadn't heard of Synapse but its the most unobtrusive data-binding lib I've seen so far. Very cool indeed!
As far as implementation goes, it still feels "wrong" to me. I love syntactic sugar when possible, but forcing one paradigm on another without measurable benefit (aside from one's comfort/preference) just seems superfluous to me.
I would personally implement it using a modular FP pattern, you get constructors, private methods and all those juicy bits we love about OO, except we are in a completely functional state of mind.
True that - I actually disagree with his final conclusion, because you can still program functionally in CS. I embraced JS prototypes a long time ago and have no need for the OO magic offered by CS. That said I still prefer it for a good chunk of my development.
Coffee vs JS rants typically degenerate into emotional outbursts, I was impressed by the author's overall restraint and advocacy for both approaches
CoffeeScript's classes are the extend functionality in underscore/backbone. Perhaps they're misnamed a little, but they're extremely simple. Just read the generated code.
This is my favorite reply, because it's exactly right. No one is stopping you from mixing and matching paradigms either! You can extend CoffeeScript classes with extra mixins.
I think CoffeeScript's class keyword does a great job at what it's trying to do: make all the complexities of prototypical inheritance accessible to the average person who just wants simple object-oriented classes. CoffeeScript is just JavaScript.
Which will of course be forked to LatteScript, CappacinoScript and MachiatoScript. Real non-conformists will insist on TeaScript (or its fork - ChaiScript). After years of infighting, the industry will finally get behind a standard based on the least common denominator - CaffeineScript which will prompty be sued out of existence by the Caffeine JVM (overlay on .NET?).
By then, young developers will be so disillusioned with the whole process that they'll invent something totally different ... and who can blame them ;) Due to the vocal nature of its supporters, RubyRedGrapefruitScript becomes the default scripting language available in modern browsers.
I'm glad I know assembly language ... I can always go back to the beginning ;)
Haha ... I only knew about a couple of them but that's quite a list. And we haven't even started listing the coffee-named projects related to Java (like Kaffe - http://www.kaffe.org/). I think I'd better quit now and get some code written!
I'd say this "difficult-to-debug" attitude comes from those who did not actually tried to work with CoffeeScript and just think that would be the case.
We have a lot of code in it already and debugging was never an issue.
Sorry, but wouldn't that defy the purpose? You can only use JavaScript client-side, and I assume it would take a lot of time, effort, and "why" to make browsers ship parsers for other languages.
I'd hardly characterise coffeescript generated js as difficult to debug (even the OO flavoured bits). My experience and that of most others I've seen online is that the mapping from the js that you debug back to the coffeescript that generated is trivially done in your head.
For example:
What? Instances of CoffeeScript classes are just like any other JavaScript object with a prototype -- just like any old string or function or array. The "language contrivance" `@` symbol is simply an alias for `this`. Nothing more and nothing less. `@name` in CoffeeScript is just `this.name` in JavaScript -- I'm not sure how you wind up with "functional referential transparency in anonymous contexts" from there ... but I'll have a little of whatever Leon's smoking ;)There's many other things in the essay, but the core boils down to this: There are a bunch of folks who are used to ignoring prototypes in JavaScript because they hand-roll objects by manually adding properties or using Crockford's "module pattern". For them, CoffeeScript's `class` keyword feels like it's adding something alien to JavaScript because it makes prototypes accessible. It's not. Using prototypes and objects lies at the core of JavaScript, and it's only the difficult and fragile syntax that leads people to manufacture new instances like this:
Instead of the far more efficient: Finally, CoffeeScript isn't encouraging you to do anything in particular. If you truly don't like using prototypes in JS, feel free not to use `class` in CS. Ping me the next time you're in NYC. Always happy to grab a drink and talk shop.