My Perception of CoffeeScript
leongersing.tumblr.com
leongersing.tumblr.com
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:
var makeDog = function() {
return {
bark: function(){},
run: function(){},
...
};
};
Instead of the far more efficient: new Dog;
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.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.
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.
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.
"Stop hitting yourself with a hammer."
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.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?)
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.
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.
But these are all features you can blithely ignore: don't use backticks, and stick to native JS prototypes and the thin arrow.
At which point you're left with, from where I'm sitting, a less noisy, more concise, more enjoyable way to program the web browser than plain JS.
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 :-)
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.
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.
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.
I'm curious, which tools can you use for this?
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.
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."
His example is known by various other names, most commonly http://www.google.com/search?q=lots+of+little+objects
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."
So pick your poison.
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.And that's not a bad thing.
If you want FP and encapsulation, you can write something like this (functionally equivalent to the OO version, unlike TFA's example)
move = ->
alert "#{@move_verb or "Mov"}ing..."
alert "#{@name or "It"} moved #{@distance or 1} meters"
snake =
name: "Sammy"
distance: 5
move_verb: "Slither"
horse =
name: "Tommy"
distance: 45
move_verb: "Gallop"
move.call snake
move.call horse (def snake {:name "Sammy"
:distance 5
:move-verb "Slither"})
(def horse {:name "Tommy"
:distance 45
:move-verb "Gallop"})
(defn move[{:keys [name distance move-verb]}]
(println (str (or move-verb "Mov") "ing..."))
(println (or name "It") "moved" (or distance 1) "meters"))
(move snake)
(move horse) val snake = Map("name"->"Sammy", "dist"->"5m","move"->" slithers ")
val horse = Map("name"->"Tommy", "dist"->"45m","move"->" gallops ")
def move(m: Map[String,String]) = println( m("name") + m("move" ) + m("dist")
scala> move (snake)
Sammy slithers 5m
scala> move (horse)
Tommy gallops 45m
If FP = OO via dictionaries, then yeah, ok :))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):
-module(animal).
-record(animal{
name :: string(),
distance :: integer(),
move_verb :: string()
}).
-spec move(animal()) -> ok.
move(#animal{name=Name, distance=Distance, move_verb=MoveVerb}) ->
io:format("~sing... ~s moved ~b meters~n",
[default(MoveVerb, "Mov"), default(Name, "It"), default(Distance,1)]).
-spec default(X::any(), Val::any()) -> any().
default(undefined,Val) -> Val.
default(X, _) -> X.
-spec main() -> ok.
main() ->
Snake = #animal{name = "Sammy",
distance = 5,
move_verb = "Slither"},
Horse = #animal{name = "Tommy",
distance = 45,
move_verb = "Gallop"},
[move(A) || A <- [Snake, Horse]],
ok.As a result, I'm increasingly inclined to do data-structure objects and composable functions, which seems similar to Go's approach.
1 http://perfectionkills.com/instanceof-considered-harmful-or-...
Objects are a poor man's closures.
Source: http://c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent. Be sure to click through to the original source thread. It's a fascinating discussion.
https://github.com/bruth/synapse/blob/master/src/synapse.cof...
Here's the documentation, to get some context:
http://bruth.github.com/synapse/docs/#learn
Do you feel that this code would be significantly improved by using JavaScript's bare prototype pattern instead?
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.
http://answers.oreilly.com/topic/2177-how-to-use-the-module-...
I'd much rather leverage the core strength of a language than try to force it to fit my own habits/patterns. That's just me though :)
Coffee vs JS rants typically degenerate into emotional outbursts, I was impressed by the author's overall restraint and advocacy for both approaches
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.
There are lots of people who don't even include inheritance in their definitions of OO.
On a more serious note, I love Coffeescript and am adept at Javascript. But since we have both of them, I get to choose.
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 ;)
Coco: https://github.com/satyr/coco
LiveScript: http://gkz.github.com/LiveScript/
IcedCoffeeScript: http://maxtaco.github.com/coffee-script/
Parsec CoffeeScript: https://github.com/fab13n/parsec-coffee-script
UberScript: https://github.com/jstrachan/coffee-script/blob/master/TypeA...
ToffeeScript: https://github.com/jiangmiao/toffee-script
Kaffeine: http://weepy.github.com/kaffeine/
The syntax is beautiful, it had potential; why be a difficult-to-debug JavaScript 'compiler' instead?