jashkenas / coffeescript

Unfancy JavaScript
https://coffeescript.org/
MIT License
16.5k stars 1.99k forks source link

Unnecessary parentheses in function calls. #1407

Closed jussiry closed 10 years ago

jussiry commented 13 years ago

Currently foo bar .baz() compiles to foo(bar.baz());

This seems unlogical, since the same result can be gained with cleaner code by writing foo bar.baz().

So should't it rather compile to foo(bar).baz();? This would allow us to write code with even less parenthesis in many situations (something i'v really learned to love with CoffeeScript).

erisdev commented 13 years ago

What compilation would you recommend for a b c .d()? Should it be a(b(c)).d() or a(b(c).d())? Should a .b() then be compiled as a().b()?

What about this common use case for jQuery? What would the whitespace do here?

$('a')
  .button()
  .click(load_ajax)
michaelficarra commented 13 years ago

So you're suggesting a function call's argument list ends when it sees a dot-style property access but only when there's whitespace before the dot (which, I'll remind you, is just JS)?

> obj.f0 a0, a1, a2 .f1 b0.b00, b1 .f2 c0
obj.f0(a0, a1, a2).f1(b0.b00, b1).f2 c0

I can kind of see why someone might want to write that the first way (DSLs maybe?), but it is quite a bit more readable with explicit parentheses. -1 for now.

jussiry commented 13 years ago

erisdiscord, i think a b c .d() should compile to a(b(c)).d() (property after . binds to the first function).

EDIT: now that i think of it, a .b() should just compile to a.b(). Like in CoffeeScript in generals, without parenthesis function is executed only when it's given parameter(s).

jussiry commented 13 years ago

michaelficarra, yes, but JS is ugly. ;) I agree that the second example is cleaner and I hope that no one writes the code in real life with as many parameters without parenthesis. But the point is not to allow long lists of parameters without parenthesis (as long as there's a clear logic on how they would compile if someone wrote them), but to allow cleaner code in simpler examples.

satyr commented 13 years ago

Interesting. There would be enough visual hint since you don't usually space before ., and you can still write a b. c to mean a(b.c). Similar to the new "command chain" in Groovy 1.8.

thejh commented 13 years ago

:+1: Increased craziness:

a b c .d()

compiles to

a(b(c).d())

and

a b c  .d()

compiles to

a(b(c)).d()

(good luck if you don't like fixed-width fonts :P )

erisdev commented 13 years ago

@thejh that's horrifying.

thejh commented 13 years ago

@erisdiscord I'd rather call it "consistent".

thejh commented 13 years ago

Humm... on a related note, what would you think about doing operator precedence using spaces? Something like

1+1 * 4 # 8, not 5
erisdev commented 13 years ago

@thejh I call it "pretty difficult to read". I'm not really a fan of the original proposal and I can't see this version bringing any additional value. -1 :C

jussiry commented 13 years ago

@thejh, why would it make a difference if there's one or two spaces before dot (.)? As i see it, with this proposal a b c .d() would also compile to `a(b(c)).d()``.

jussiry commented 13 years ago

@thejh, on the operator precedence: i like it. :) Haven't given it that much thought, though.

jussiry commented 13 years ago

To return to the jQuery example presented by @erisdiscord:

$('a')
  .button()
  .click(load_ajax)

Would work just as it now does, but with the new rules you could actually write it without the parenthesis:

$ 'a'
  .button()
  .click(load_ajax)

Which isn't possible in CoffeeScript at the moment. The only problem would arrive if you would add another function before $:

another_func $ 'a'
  .button()
  .click(load_ajax)

Because in this case the .button() would be executed on another_func, but i don't see this being a very common use case.

ricardobeat commented 13 years ago

It gets really unreadable after a few methods. Consider this:

get(words).split(' ').filter((w) -> r.test w).join(', ')

It would be written as

get words .split ' ' .filter (w) -> r.test w .join(', ')

You just borked the function expression. This could be the case for a custom operator, we don't have many options since | is taken:

get words ..split ' ' ..filter (w) -> r.test w ..join(', ')
$.get '/api' ..success fn ..error fn

// overload then
foo bar then .baz()
get words then .split ' ' then filter (w) -> w then join ', '

// spermatozoid operator
foo bar ~> .baz()
get words ~> split ' ' ~> filter (w) -> w ~> .join ', '

But it doesn't make things much better, besides being a huge departure from "normal" syntax.

ricardobeat commented 13 years ago

One that is kind of ok is re-using @:

foo bar @ baz()

This would be different from foo bar @baz() in the same fashion that foo 1 + 2 is not the same as foo 1 +2. And it would re-use the @ semantics of being this, in this case this is the result of the left-hand expression.

For clarity the dot could be kept: get words @.split ' ' @.filter (w) -> w @.join ', '

satyr commented 13 years ago
get words .split ' ' .filter (w) -> /bacon/.test w .join(', ')

You just borked the function expression.

Only because you mixed implicit indentation (inserted after ->, consuming the rest of the line) which obeys its own, different rules. One-liner functions must be parenthesized in such cases.

ricardobeat commented 13 years ago

@satyr but that kind of defeats the purpose of not having to nest things in parenthesis.

yfeldblum commented 13 years ago

"Significant whitespace" speaks primarily about significant linebreaks and significant indentation. These are readable. Not all whitespace should be significant. Some types of whitespace should not be significant: such as the whitespace within a line.

satyr commented 13 years ago

Some types of whitespace should not be significant: such as the whitespace within a line.

Already significant beyond help:

$ coffee -bpe 'a? b ? c?'
if (typeof a === "function") {
  a(typeof b !== "undefined" && b !== null ? b : typeof c !== "undefined" && c !
== null);
}

$ coffee -bpe 'a[b...] [c...]'
var __slice = Array.prototype.slice;
a.slice(b)(__slice.call(c));
michaelficarra commented 13 years ago

Yeah, we are pretty big on the significant whitespace here. Still, this one's a bit of a stretch. I could live with it, but it's nowhere near as intuitive or readable as our other instances of significant whitespace.

jussiry commented 13 years ago

I agree that binding the property after the dot notation to the first function in the command line (or the one after the previous dot notation) allows you to write code that is hard to read. But there's no programming language that can stop people from writing bad code. So i guess my question is this: is there ever a time when someone would write foo bar .baz() currently in CoffeeScript? Sure, we chain the commands with empty space between, but in that case we always put the next command on the next line. And even when you take the one real world case that would brake with the new notation...

another_func $ 'a'
  .button()
  .click(load_ajax)

...I actually find it much more logical that .button() would bind to another_func:tion, since this allows us to process the whole function line at once, and only after that move to the next lines. Currently it's slightly backwards since you first have to process $('a') in your head, then .button(), then .click(...) and only then jumping all the way back to another_func.

yfeldblum commented 13 years ago

Yep, CoffeeScript has intra-line significant whitespace. But maybe it's a good idea not to increase the quantity of it, especially by adding an easily confusable and barely debuggable significance of whitespace affecting the order of operations.

erisdev commented 13 years ago

Actually, while I am not too keen on a b .c() becoming a(b).c(), I do like the cut of that multi-line jib.

$ 'a.popup'
    .button()
    .click -> popup this.href

Is it reasonable for a newline to close the outermost implicit parentheses in this case?

TrevorBurnham commented 13 years ago

This came up on Stack Overflow recently. It would mean that you could write

js = require 'coffee-script' .compile coffee

which is pretty nice. And people have long been begging for

$ '#sprite'
  .html str
  .css left: x, top: y
  .bind 'click', clickHandler

to be supported somehow. The main counterargument Jeremy and others have expressed toward such proposals is that they overload indentation with multiple meanings. This proposal, on the other hand, makes the indentation non-significant, the same way that

a +
  b

is a perfectly acceptable way of writing a + b. I find that to be consistent and beautiful.

Let's remember that "it allows ugly code" isn't a compelling argument against a language feature—ugly code will always be possible. The question is, rather, "Does it encourage ugly code?" And this proposal, in my view, does the opposite, allowing more stylistically consistent code to be written. An enthusiastic +1 from me.

reissbaker commented 13 years ago

I would really, really like this to happen. There isn't a great reason to write foo bar .baz if you mean foo bar.baz, but there are a lot of reasons to write jQuery- or connect-style method chains. CoffeeScript already promotes aesthetic structure as defining behavior; this just makes the structure more beautiful and more expressive.

tcr commented 13 years ago

I wanted to propose why this issue is more in line with CoffeeScript's philsophy by preserving significant whitespace. Right now, CoffeeScript is less ambiguous in one way because it converts

brush
    .startPath 'red'
    .moveTo 10, 10

into

brush.startPath 'red'.moveTo 10, 10

where the . operator responds to the trailing argument in the previous line. Yet when reading this code, it is ambiguous to which object we are referring. CoffeeScript will currently let you can nest method calls for different objects in the same indentation level (brush vs the string 'red'), which seems unintuitive. Indentation, or nesting level, should really imply statements are in the context of the most recent line of a lesser indentation level. Reading it in this way, each method call refers to brush.

Depending on how #1495 is resolved, this definition may not extend to nested chaining syntaxes (though parentheses could still be omitted); but one possibility to get the current behavior is to make greater indentation override the implicit parentheses:

brush
    .startPath 'red'
        .moveTo 10, 10

Compile to brush.startPath 'red'.moveTo 10, 10 as before.

quangv commented 12 years ago

OP @jussiry, +1

I just wanted to add the main Benefit of supporting this feature, is to not have to go backwards into your code, to have to add parenthesis to do something simple.

js: x('hi') ~> x('hi').y

coffee: x 'hi' ~> x 'hi' .y doesn't work >:[

... have to go backwards and add parenthesis x('hi').y

wth, why can't I write it this way x 'hi' .y !?!

if I meant x('hi'.y) I would've written in coffee x 'hi'.y !!! (no-space after 'hi')

This happens a lot! (having to go backwards just to add parenthesis ... especially in client-side jQuery code and other libraries that does chaining... it's really annoying to have to go backwards...


And I think yes, white-space is important, actually I think any character in programming is important... every ; every " every _ ... I believe we as efficient programmers, likes code, with as minimum amount of characters as possible, that's still readable/maintainable.

We account for every character, trying to get rid of as much as possible.

Coffee-Script was made I believe, to make coding JavaScript more joyable and efficent, one of the main benefits is a cleaner syntax, with less ; ( ) { } ...

I think Coffee-Script programmers, translates _white-space_ after function/method, in their minds to mean (parenthesis) ...

x y    => x(y)
x.y z  => x.y(z)
x y .z => x(y).z
erisdev commented 12 years ago

@quangv If you hate all the punctuation and stuff and prefer whitespace, you might be interested in this other language. ;D

quangv commented 12 years ago

@erisdiscord why does CoffeeScript translate x 'hi' into x('hi') ?

Why not just keep the parenthesis?

mcmire commented 12 years ago

@quangv I'm going to guess that CoffeeScript's allowance for omitting parentheses, as well as its precedence rules in terms of missing parentheses, come directly from Ruby. Which I think is sensible. (It also translates x y .z as x(y.z), by the way)

I do not think people translate spaces to mean parentheses. I think they read it to mean:

/(\W+)\w+(\W+)/ => $1 + "(" + $2 + ")"

which is pretty easy to remember if you think about it. So the following expressions are pretty straightforward this way:

a b         => a(b)
a b c       => a(b(c))
a.b c       => a.b(c)
a b.c       => a(b.c)
a b.c d e.f => a(b.c(d(e.f)))

That said, x y .z admittedly has an ambiguous interpretation, but only if you think about it different ways. If you hold to the rule above, then you could think about it this way:

x y    => x(y)
x y .z => x(y(.z)) # uh oh, invalid! try removing the level of parens we just added
       => x(y.z)   # ok!

Or, something like this, which is probably closer to how CoffeeScript actually parses x y .z:

"x"  -> ["x"] # yay identifier
" "  -> ["x", ??] # hmm, maybe this is a function? i'll wait
"y"  -> ["x", "y"] # this must be a function arg. push onto stack
" "  -> ["x", "y", ??] # another upcoming arg?
".z" -> ["x", "y.z"]   # huh! this starts with a dot. welp, it can't be an identifier, identifiers can't start with a dot. i'll just append to the prev token

["x", "y.z"] => "x(y.z)"  # code translation

Translating whitespace as a placeholder for parentheses is fine for the cases that work:

x y         => x(y)
x y.z       => x(y.z)
x.y z       => x.y(z)
x y .z      => x(y).z

but doesn't work for everything:

x y z       => x(y)z ??
a b.c d e.f => a(b.c)(d)(e.f) ??? oh good grief
jussiry commented 12 years ago

@mcmire, addmittedly this could add some complexity to the current way of parsing code, but the idea would be to use [whitespace].foo as special case that would causes the expression before that to be evaluated, before continuing with the parsing.

This would cause one weird situation though: how to parse if there is white space on both sides of the dot? I'd just throw a parse error in this case, since i can't see any reason for using such a syntax.

quangv commented 12 years ago

@mcmire

(It also translates x y .z as x(y.z), by the way)

lol, yeh I threw that in there because x y .z seems like it should translate to x(y).z

Also I just meant translate as a mental image of the code, not a literal translation of _ space to mean ( ) ...It's easy enough to know that [space]name to mean (name

perhaps it's better to look at [space] to mean just the opening ( parenthesis, and it just closes on it's own... and [space]. force closes the most previous (, special case like @jussiry said.

I like your parenthesis examples.

x y .z => x(y(.z)) invalid! but at least i "makes sense" in mind?

but still if I wanted to write x(y.z) in coffeescript I'd write it x y.z, I wouldn't include unnecessary space

x y z => x(y)z ??

nope it'll be in my mind x(y(z and coffee compiles to => x(y(z))

a b.c d e.f => a(b.c)(d)(e.f) ??? oh good grief

This would be a(b.c(d(e.f in my mind and compiles to => a(b.z(d(e.f)))

Just wanted to point out

It's currently _impossible_ to write x(y).z in CoffeeScript without (parentheses). This is an injustice for me as a CoffeeScript parentheses-function-call-hating programmer ... there should be a way.

x y .z  # nope

x y
  .z    # nope

... that's the thing that gets me... I <3 CoffeeScript, it has trained me, to _not use_ parentheses for function/method calls...

x 'hi'
x.y 'hi'

but the only time I have to use parentheses again is for chaining! and then I have to go back and _add that ( ...

x(y).z  # ahh what the freak, I thought I was rid of those ( ) !!! >:[   

It's so freaking weird while programming to write

x y and then wantin x(y).z

# the only way to write this syntax in CS is to write it the exact same way in JS

It feels unnatural as a CoffeeScript programmer to have to add ( and ) just to chain .z onto x y ... especially when chaining is so common on client-side coffeescript/javascript programming ... you have to go backwards a lot... very annoying to me.

michaelficarra commented 12 years ago

the only way to write this syntax in CS is to write it the exact same way in JS

Not exactly. I would have written your example as (x y).z. I find that much more natural because x y produces a value, upon which we are using the member access operator ..

quangv commented 12 years ago

@michaelficarra (x y).z

ah good point... you still have to use ( ) though... and when the code started as x y, it's really annoying to have to add those, and change code to (x y).z

michaelficarra commented 12 years ago

@quangv: Yes, I agree. That's a very common bother for me. This syntax seems like a pretty good solution, although not the most readable. But I'd probably make use of it.

mcmire commented 12 years ago

This is an injustice for me as a CoffeeScript parentheses-function-call-hating programmer

See, this I think is a mistake that many people who've jumped on the CoffeeScript bandwagon have made. A thing I learned a while back is just because CoffeeScript lets you do something, doesn't mean you should do it everywhere. Just as you have the option to remove parentheses, you also have the option to leave them in where it would be more readable. Same goes for one-line if statements, comprehensions, etc. CoffeeScript gives you a lot of tools and it's quite easy to abuse them.

I'm not disagreeing with you that it's annoying that you have to add back parens in the x(y).z case. I just don't think the solution is to allow leaving out the parens, I think the solution is to get into the habit of using parens for require statements only. This is what I do, it's only two extra characters (!), and it's future-proof.

The only case I see where x y .z makes sense to be interpreted as x(y).z is when written

x y
  .z

(it's still kinda weird but line breaks actually mean something in CoffeeScript so it seems okay). In that light, perhaps x y ; .z is the solution here. So you could write require "foo" and then come back later and write require "foo" ; .bar (or require "foo";.bar if you wanted to save two characters) and boom, problem solved.

erisdev commented 12 years ago

@mcmire Exactly. It's like a Perl programmer writing all of their conditionals like this because Perl allows it:

do { stuff();
     moreStuff() } if (condition);

@quangv I was kidding! But really, CoffeeScript isn't about getting rid of parentheses. The parenthesisless syntax for function calls is prettier and possibly more readable on its own and it allows us to make nicer-looking DSLs, but stuff like foo 37 .bar() is harder (for me) to head-parse than foo(37).bar().

quangv commented 12 years ago

@mcmire I think the solution is to get into the habit of using parens for require statements only.

Oh I use chaining in way more things then require statements... as I learned from #1495 (the multi-line discussion of chaining... I consider this thread the single-line discussion of chaining) for require to use CoffeeScript's destructuring assignment

{bar} = require 'foo'

This is an injustice for me as a CoffeeScript parentheses-function-call-hating programmer

I'm just saying that while writing CoffeeScript codes, I've been train and got into the habit of _not_ using parentheses for function/method calls... and it's a pain to have to go back into my existing code, add parentheses, just to make chaining work.

yfeldblum commented 12 years ago

In other languages/communities, the general conventions seem to tend towards:

Even though the language lets you skip parentheses at times, that's not the go-ahead to skip parentheses all the time.

quangv commented 12 years ago

@yfeldblum, what do you think the CoffeeScript way should be?

CoffeeScript is inspired by Python

In Python, there's the credo

"There should be one-- and preferably only one --obvious way to do it."

Now does the below seem to go with or against this principal ?

x y .z # => x(y.z)
x y.z  # => x(y.z)

I think the above is an oversight, unless you can find somewhere or someone who thinks the above is a feature, to let you do, dot notation with space in-between...

The status-quo isn't good enough sometimes, if CoffeeScript wanted to be JavaScript, it wouldn't exist.

quangv commented 12 years ago

@erisdiscord but stuff like foo 37 .bar() is harder (for me) to head-parse than foo(37).bar().

Yes it is, but it's easier to program.

The act of actually adding .bar onto foo 37, is much, much simpler, and less annoying, if foo 37 .bar() was supported instead having to, use your mouse, or keyboard, changing that [space] after foo to a (, then going back to where you was, end of the line, adding ).bar()...

ahh, annoying just to think about it... it totally _kills the flow_ of you programming.

Looking at other people's code, even looking at your old code, it takes a bit to grok it, but while programming, if you type foo 37 ... then you add .bar() ... you have 100% clarity as to what it is you want and expect...

But yeah I don't think CoffeeScript's goal is to have the must beautiful, understandable code in the world (maybe it might be...) but I think it's bigger goal is having one of the best programming experience in the world...

If we wanted 100% understandable, no margin for confusion code, we'll use parenthesis all the time.

foo(37) is much more self-explanatory then foo 37 ... so I ask again, why does CoffeeScript support omissions of parentheses on function/method calls?

maybe CoffeeScript needs a Philosophy ? ...

showell commented 12 years ago

@quangv CoffeeScript's Philosophy with parentheses isn't that hard to interpret. You can put parentheses in lots of places, even in places where it's only to help the reader. But, unlike Python, CS doesn't require you to put them in places where there's a natural order of operations. Python doesn't require you to write "(5 * 3) + 2" in order to get 17; you are allowed to write "5 * 3 + 2". OTOH Python won't let you write "sum x, y", even though that many folks would say that code has one pretty obvious interpretation. CS just gives you a little more flexibility. It's really not that big a deal.

The whole argument about having to backtrack seems kind of specious to me. If I say x + y, I don't have to surround it in parentheses. But then if I want to double the expression, I have to backtrack and write "(x + y) * 2". That's true even in Python.

satyr commented 12 years ago

maybe CoffeeScript needs a Philosophy ? ...

We got some:

quangv commented 12 years ago

@satyr

  • What Ruby does can't be bad.
  • The more your code reads like English, the better.

Nice... where you get those?

@showell ... interesting interpretation, very laissez-faire... parentheses are optional, only when they are not... my only comment is (x + y) * 2 has a very known order of operations... what's the order operation of x y .z ...

I still don't like how that [space] in between y & .z means nothing...

It's like the [space] in between x & y, means something, why not the later?

quangv commented 12 years ago

What do you guys think about this, for single-line & multi-line chaining support.

x y .z
# => x(y).z
x y
  .z
# => x(y).z
mcmire commented 12 years ago

CoffeeScript is inspired by Python

Well, CoffeeScript is inspired by a few languages. Comprehensions, significant whitespace, and leaving off "end's" come from Python, the rest comes from Ruby/Perl and some of the Harmony/ES6 proposals.

maybe CoffeeScript needs a Philosophy ? ...

I think it already does, really: be a better version of JavaScript. CoffeeScript starts with JavaScript and makes it suck less by adding 'modern' features that have already been well accepted in other languages.

In any event, your example (against Python's motto) doesn't really fly because x y .z isn't an intended alternate way to write x y.z, it's an edge case, something that the parser is forced to handle because it isn't a feature of the language. Actually the whole parentheses thing is a red herring -- the edge case is x .y. There's two ways to handle this -- either convert it to x.y, or fail with a parser error -- and obviously we know what CoffeeScript does. I mentioned that its behavior comes from Ruby, but I just checked and Python not only does this, but more importantly, JavaScript does as well. (Where JavaScript got it from, I'm not sure, perhaps C?) So while it's kind of silly -- honestly I would expect a parse error -- that's what happens. It's a historical thing. (Although, not entirely illogical; after all, you can say foo[bar] just as you can say foo [bar]... so in that light foo .bar is just being consistent. You can also say foo \n .bar or foo ; .bar, so foo .bar is also being consistent there too.)

Normally this behavior would be a non-issue and could be "fixed", because being an edge case and not a feature, no one would (should) actually write x .y in their code on purpose, and that's as complicated as an expression like that can get without creating a parse error (because x y .z is not valid JavaScript), so handling it in a weird way is inconsequential, so no one would (probably) mind if you changed it.

It's only consequential now because CoffeeScript has the ability to fill in missing parentheses.

my only comment is (x + y) * 2 has a very known order of operations... what's the order operation of x y .z ...

Exactly -- which takes precedence, placement of parenthesis or autocorrection of method calls. So going back to my take on the "philosophy" of CoffeeScript -- I think when ambiguity arises like this, the correct behavior would be to do what JavaScript does first, then layer CoffeeScript-specific behavior on top of that. That's why I think x y .z should remain as x(y.z) -- because with parenthesis, JavaScript would parse x(y .z) as x(y.z), so parsing x y .z as x(y.z) is just the next logical step. In other words, apply the JavaScript rule first, then CoffeeScript. I think this is true to the design of CoffeeScript itself, if you look at other things in CoffeeScript.

You asked why does CoffeeScript let you omit parentheses and I say, for the same reason that it drops parentheses around if statements and lets you write foo if bar or foo for foo in bar. These features serve their purpose when you use them if 1) you write less code and 2) your code is more readable in the process. If you write less code but your code is less readable, then it is probably not wise to use them. (Yes it is true that it is easier to write, but then again, all code is easier to write than read. Always.) In the case of require "foo" .bar it is less readable only because we have been trained to expect (through previous languages that use dot notation for OO code, and just, I think, the natural way that people read code, which is left-to-right) that a dot is connected to whatever comes immediately before it, and that whitespace (well, spaces) in this scenario are insignificant and should be ignored.

satyr commented 12 years ago

Nice... where you get those?

Jeremy's mind.

quangv commented 12 years ago

@mcmire

Thanks for thorough reply. A lot of the reasons you make against proposed [white-space][dot] syntax could be had in support of the syntax.

First let me clear up:

"For x .y, I think we can look at it like (x).y and compile it to x.y =p" #1495

I totally think x .y should still be x.y ... weird I know, hah but it makes the most sense to me. if we wanted to chain x, write it as x().y ... there's no annoying back-track-coding that takes place.

x y .z isn't an intended alternate way to write x y.z, it's an edge case, something that the parser is forced to handle because it isn't a feature of the language.

Totally agree with you. It's an 'oversight', 'toleration', not really a 'feature'.

Thanks for checking how Ruby, Python handles this. I kinda agree that throwing an error might be worse than just pretending that the space isn't there... but that's up for debate as well. (like JS supporting lines without ending in ;) ... whoa foo [bar] is worst then foo .bar!

Normally this behavior would be a non-issue and could be "fixed", because being an edge case and not a feature, no one would (should) actually write x .y in their code on purpose, and that's as complicated as an expression like that can get without creating a parse error (because x y .z is not valid JavaScript), so handling it in a weird way is inconsequential, so no one would (probably) mind if you changed it.

Yes, well put! Hopefully not... x .y i think should still be x.y, but x y .z compiling into x(y).z would make a lot of us salivating for a chaining syntax happy. :)

I think when ambiguity arises like this, the correct behavior would be to do what JavaScript does first, then layer CoffeeScript-specific behavior on top of that. ... I think this is true to the design of CoffeeScript itself, if you look at other things in CoffeeScript.

Yes, I agree, default to JS when there's no CoffeeScript-specific behavior. Hopefully we can have a CoffeeScript-specific chaining syntax for everyone who wants it, but inconsequential enough that people who don't use this new CS-specific syntax, won't be impacted by it, and all their current CS code, compiles and works the same... tall order, but hopefully we can get-her-done... back-track-coding is just way too annoying.

You asked why does CoffeeScript let you omit parentheses and I say, for the same reason that it drops parentheses around if statements and lets you write foo if bar or foo for foo in bar. These features serve their purpose when you use them if 1) you write less code and 2) your code is more readable in the process. ... In the case of require "foo" .bar it is less readable only because we have been trained to expect that a dot is connected to whatever comes immediately before it, and that whitespace (well, spaces) in this scenario are insignificant and should be ignored.

:)

I think this x y .z => x(y).z agrees with your 1., less code, and 2. readability is in the eye of the beholder, and I also agree with you that once we are trained of this new behavior, it'll be that much easier to understand the code.

Some non-Coffee-er cold argue foo(bar) is easier to read than foo bar ... but I don't think any Coffee-er is willing to give up this great syntax... CoffeeScript's makes white-space significant and it's clever use of white-space is one of the reasons I think that makes it so awesome.

@satyr I think it should be written somewhere... I believe more lexical conflicts of syntax, taste, and grammar would come up in the future that maybe a simple "doesn't comply with our Philosophies" would be a great response... and people can argue the merit of a particular principal before a proposed syntax enhancement or change. The FAQ is a great lift-off point and the official docs has philosophies and principals littered through-out. :) Keeping CS awesome I think is everyone's main goal...

mcmire commented 12 years ago

@quangv So this is interesting, look what coco does: http://satyr.github.com/cup/#c:a%20b%20.c%20.d%20e /cc @satyr

quangv commented 12 years ago

@quangv So this is interesting, look what coco does: http://satyr.github.com/cup/#c:a%20b%20.c%20.d%20e /cc @satyr

Oh wow, that's cool! Nice job @satyr ... I didn't realize what Coco was...

http://satyr.github.com/cup/#c:x%20y%20.z awesome, perfect... coffeescript should compile this way too.