[1]http://www.jwz.org/blog/2010/10/every-day-i-learn-something-...
I understand it was just a stupid joke but when your stupid joke insults a person who deserves praise you should probably reconsider the joke.
The point of the JS bashing in the slides is that the Bad Parts do exist, and CoffeeScript does a great job of routing around them, as does Strict Mode.
Arrays could use a beard or the varied language syntax is going to cause ingrown pains:
Declaring arrays
In CoffeeScript:
things_that_rule = [
"robot unicorns"
"honey badgers"
"Lord Inglip"
]
Destructuring assignment
Assign variables from inside arrays:
sum_and_difference = (a, b) -> [a + b, a - b]
[sum, difference] = sum_and_difference 5, 2
It's also bizarre from a Python background: >>> [
... "a"
... "b"
... "c"
... ]
['abc']
===== if foo.bar? then console.log foo.bar
# if (foo.bar != null) { ... }
versus if(foo.bar){console.log(foo.bar);}It's true, every time I see CoffeeScript vs JavaScript examples, they're often unfair. Some addressed in this older thread[1].
Another thing I don't understand is why make the parenthesis for function calls optional? Such as
sum_and_difference 5, 2
Especially since one of the most common complaints about JavaScript is ambiguous inconsistency (var, semicolons).Ok, so parenthesis are optional,
foo = myfunction()
calls myfunction, but foo = myfunction
does not? I suppose this is better than the way Ruby does it where you have to jump through hoops to get a reference to your function, but is this really better than making parenthesis for calls mandatory? {createServer} = require 'http'
host = '127.0.0.1'
port = 1337
server = createServer (req, res) ->
res.writeHead(200, 'Content-Type': 'text/plain')
res.end('Hello World\n')
server.listen(host, port)
console.log "Server running at http://#{host}:#{port}/"
Notice `require` looks sort of like a language statement. The `console.log` thing is my own quirk. However, the call to `createServer` would require a hanging paren if parens weren't optional. It wouldn't look bad here (and, in fact the official example uses it to chain the listen call) but is less attractive if you're using longer functions. Calls like `memoize` and `operation` (think unit of work pattern) tend to work well with this.foo = object.method(:method_name)
foo.call
foo = myfunction
Considering the higher-order nature of Javascript programming having quick access to refs is important.The Coco dialect of Coffee suggests that a bang could be used to denote a call:
foo = myfunction!foo = do myfunction
> Arrays could use a beard or the varied language syntax is going to cause ingrown pains
The whitespace magic only causes me a problem in a few situations.
Watch indent on wrapped if statement:
if foo is true or bar is false or
baz is null
doSomething() #Error
if foo is true or bar is false or
baz is null
doSomething() #ok
Explicitly returning objects is awkward: foo: ->
if not ok then return
#lots of code here
return
x: 1 #ha ha
y: 2
foo: ->
if not ok then return
#lots of code here
return {
x: 1 #ok but ugly
y: 2}
I always want to use implicit call parens with property chaining but that doesn't work: $('.doAllTheThings').filter 'span'
.data 'foo', true
.wrap '<div/>'
#produces filter('span'.data('foo' ...
$('.doAllTheThings').filter('span')
.data('foo', true)
.wrap('<div/>')
The result of assigning a comprehension threw me off for a while because I was thinking Python list comprehension instead of postfix loop: foo = x + 1 for x in array
foo = (x + 1 for x in array) #what I expected
(foo = x + 1) for x in array #what happens
for x in array #same thing written the other way
foo = x + 1
Finally, the whitespace magic working well tends to depend on object/functional arguments coming last in the call list with the functional arg coming after the object.These annoyances are relatively rare and are the sort of thing you just get used to if you use the language heavily. The upside is that for an appropriately designed API (the options object argument last convention in JS makes this fairly common), the object literal magic gives a feel like python kwargs:
animatedShow node, duration: 200, onComplete: -> fire 'doThing'
You can also do DSLish things fairly easily. I'm a fairly heavy YUI user. Compare: YUI.add('myCoolModule', function(Y, name) {
Y.CoolModel = Y.Base.create('coolModel', Y.Model, [], {
aMethod: function(){return this.get('foo') === 1}
},{
ATTRS: {
foo: {value: 1},
bar: {value: 2, readOnly: true}
}
})
}, '1.0.0', { requires: ['app', 'model'] });
to: YUI.module 'myCoolModule',
version: '1.0.0'
requires: ['app', 'model']
(Y) ->
Y.CoolModel = CoolModel
class CoolModel extends Y.Model
@ATTRS:
foo:
value: 1
bar:
value: 2
readOnly: true
aMethod: -> @get('foo') is 1
It requires a one word patch on Coffeescript (superclass name in generated output) and the addition of a tiny YUI.module to YUI.add shim but I think the result is a lot cleaner.> It's also bizarre from a Python background
Python's auto-concatenation of runs of string literals is convenient but I don't know of another language that does the same thing.
> if foo.bar? then console.log foo.bar
The ? is an explicit existence check, while doing `if(foo.bar)` will fail if bar is an empty string or zero. I tend to write existence checks using the postfix form:
console.log foo.bar if foo.bar
This is a personal style that indicates that the normal control flow is to log but there's a simple guard on the operation. I'd use the if...then form if I expected the test to fail a significant fraction of the time (i.e. it's a branch instead of a guard). My other quirk is that I use &&/|| only for short circuiting but and/or only for boolean logic.The C preprocessor (in GCC and Clang at least) will concatenate adjacent literals, so add C, C++, and Objective-C to your list.
I've not looked closely at the implementation, but Python concatenating adjacent strings might be a relic of C-based languages -- the same rule applies there and likely a standard of the language with CPython being the default underbelly of Python.
Then your final (YUI) example, we have "random" commas. Again, the language syntax starts to trend towards generally inconsistent, and I have to enforce more style guides upon my team.
NOTE: I work heavily with YUI as well, and my team -- Python stack -- too writes shims to support "kwargs"-like syntax, so I can't really consider coffeescript having an advantage. Quick pseudo comparison...
YUI.module = function(name, func, kwargs){
kwargs = kwargs || {};
return YUI.add(name, function(Y){func(Y);}, kwargs.version, kwargs.details);
};
YUI.module("myCoolModule", function(Y){
...
}, {"version": "1.0.0", "details": {"requires": ["app", "model"]});I worry the compression, while cute, could start to get a little extreme after a while, for example:
class Shape
constructor: (@x, @y) ->
centre: -> [ @x, @y ]
area: -> 0
is starting to look close to unreadable and overcompressed (in particular, the constructor implicitly declaring member variables).Also having a quick look through the bug reports, there seems to be some interesting inconsistencies, such as:
x for x in [1,2,3] # x == 3
x for x in [1..3] # x == 4
I also see a complaint that:
foo bar: 'baz', quux('onoz')
Won't parse. I understand this should parse as:
foo({bar:'baz'}, quux('onoz'))
Both of these, and other little things I've seen, make me think coffeescript is more of a giant bag of good ideas, rather than a well-thought out language. In particular, is there a clear unambiguous grammar for it anywhere? I can't find one.
It works well enough if your API design fits the language's expectations. This generally means your function calls take arguments in the order: normal args, onlyOneObject, callback.
How well it scales is sort of a taste question. I like that getters/setters and some event handlers tend to be one line. Here's a pair of functions I actually wrote today (I'm building a proprietary wysiwyg text editor that isn't using contentEditable)
getBlocks: -> @_breakText(block.get('value'), block.getLineBreaks()) for block in @blocks
getSelection: -> try start: @input.selectionStart, end: @input.selectionEnd
Here's a random longer function: _getWords: ->
if @words.length
@words
else
@_doWrap()
words = []
pos = 0
size = 0
Y.all(@wrapSelector).each (el) ->
size = el.get('text').length
pos += size
r = el.get 'region'
words.push el: el
size: size
pos: pos
top: r.top
left: r.left
right: r.right
bottom: r.bottom
@words = words
You'll either think these are neat or that they're horrible. Note that the first is a comprehension and the second is a try/catch block returning an object (it can fail in some older gecko browsers if the page is still loading).> in particular, the constructor implicitly declaring member variables
I think this is excessively cute as well and don't use it. Nor do I use the `x = {a, b}` producing `x = {a:a, b:b}` trick, which causes problems with implicit object parsing when the object isn't the last argument to the function.
> In particular, is there a clear unambiguous grammar for it anywhere? I can't find one.
There is not and there probably won't ever be. Doing one would be complicated quite a bit by the rewriting pass that happens before the compiler starts working on the code. A grammar for the resulting, unambiguous code wouldn't be too hard but that comes from the rewriter handling most of the hard corner cases.
_getWords: ->
return @words if @words.length
# the other case of the method
this approach makes the code read almost like English.Not my experience. I love how short my code is. http://www.paulgraham.com/power.html
> there seems to be some interesting inconsistencies
Yeah, there are. These bug me less now than they did initially. Like any language, you have to know it well to use it well. But there's much less that you need to know about CoffeeScript than about, say, Ruby or Python. CoffeeScript's feature set is small enough that you can master it very quickly.
> is there a clear unambiguous grammar for it anywhere?
https://github.com/jashkenas/coffee-script/blob/master/src/g...
foo bar: 'baz', quux('onoz')
Or are you just concerned about inconsistencies?I like not having to specify parentheses, but only do so when it's immediately obvious to a reader of the code what the result of the statement will be.
Just a giant bag, period.
I agree that the interaction could be better, though.
createafunctionwiththesearguments() {...}
Your reasoning makes no sense to me.And I'm sorry, but whitespace-for-blocks really kills it for me. I can't stand it in python, but I'll suffer through it for the ecosystem. Coffeescript brings nothing to the table for me.
i.e. "lets use Javascript end to end", and then the very same people go and create a new language to replace said Javascript?
You and Alanis Morissette may call that ironic. I'd call it practical.
The flood of memes slid slowly in the back-door at reddit, one innocent step at a time. Questioning this often got you the comment that you were old, stiff and lacked the funny-gene. Time passed and look at what a worthless trollhole reddit eventually became.
I hope to see fewer submissions like this on HN in the future.