Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

method k:v if condition #1871

Closed
curvedmark opened this issue Nov 16, 2011 · 32 comments
Closed

method k:v if condition #1871

curvedmark opened this issue Nov 16, 2011 · 32 comments

Comments

@curvedmark
Copy link

method obj if condition is compiled into

if (condition) method(obj);

However, method k:v if condition is compiled into

method({
  k: condition ? v : void 0
});

I guess method(k:v) if condition is far more frequently intended than method k: (v if condition). Is it reasonable that we change this precedence?

@jashkenas
Copy link
Owner

Yes, I think it is. This is an example of the rewriter going too far, and wrapping the object around the postfix if statement ... when the postfix if statement should really be closing the implicit object.

@TrevorBurnham
Copy link
Collaborator

I bumped into this recently myself: I thought I'd be able to create an array of objects with

key: val for x in y

I feel like there's already an open issue on this... maybe @michaelficarra knows?

@paulmillr
Copy link

+1, I've got this bug today.

@jashkenas
Copy link
Owner

Yeah, I could swear this used to work properly. Perhaps a side-effect of satyr's big rewriter fixup?

@michaelficarra
Copy link
Collaborator

Nope, definitely not a recent change. I do believe there's an open issue for this. I'll try to find it.

@satyr
Copy link
Collaborator

satyr commented Nov 18, 2011

Implicit object is a diff{erent,icult} beast than implicit call in that it naturally spans multiple lines:

a: b
c: d if e

What should it mean?

  • {a: b, c: (d if e)}
  • {a: b, c: d} if e
  • {a: b}; {c: d} if e

@jashkenas
Copy link
Owner

As always, @satyr makes an excellent point. Naively, I would want:

a: b if c
# into:
{a: b} if c

a: b
c: d if e
# into:
{a: b, c: e ? d : void 0}

... but that's fairly horribly inconsistent. Perhaps our current behavior is the best option?

@michaelficarra
Copy link
Collaborator

Perhaps our current behavior is the best option?

I don't think so.

coffee -bep 'a = b: c if d'
var a;
a = {
  b: d ? c : void 0
};

Postfix if should encompass the whole line in instances like those (and the OP's)

@TrevorBurnham
Copy link
Collaborator

Agreed with Michael.

On Nov 18, 2011, at 10:47 AM, Michael [email protected] wrote:

Perhaps our current behavior is the best option?

I don't think so.

coffee -bep 'a = b: c if d'
var a;
a = {
 b: d ? c : void 0
};

postfix if should encompass the whole line in instances like those (and the OP's)


Reply to this email directly or view it on GitHub:
#1871 (comment)

@satyr
Copy link
Collaborator

satyr commented Nov 18, 2011

coffee -bep 'a = b: c if d'

Replace b: with -> and we get a similar behavior.
CoffeeScript syntax is quite complex. Simple precedences like JS can't rule it fully.

@michaelficarra
Copy link
Collaborator

@satyr: yes, it's a little more complex than my over-simplification. In that case, the current compilation is what I would desire.

@TrevorBurnham
Copy link
Collaborator

I'm going to try to cut the Gordion knot that is @satyr's example:

It seems we're all agreed that a: b, c: d if x should intuitively be equivalent to {a: b, c: d} if x, and likewise with a postfix loop; but if someone writes

func
  a: b
  c: d for d in arr

well, the intuition is very different. So our options are:

  1. Stay the course (so the multi-line object postfix stays intuitive, the single-line one stays unintuitive),
  2. Special-case it so we get both intuitive behaviors (via a hard-to-explain, whitespace-dependent rule), or
  3. Disallow one or the other.

I'm all for #3. Let's switch to the intuitive behavior for single-line postfixes, and if someone puts a postfix in a multi-line object, make them use some damned punctuation! The compiler error for the above would be something like

Parse error on line 3: Ambiguous postfix 'for' in object literal; use explicit parentheses or curly braces

@davidchambers
Copy link
Contributor

+1 for #3

@erisdev
Copy link

erisdev commented Nov 19, 2011

Agreed: 👍 for 3. If you want the old behavior, wrap the postfixed expression in parentheses.

method(k:v if condition)

func
  a: b
  c: (d for d in arr)

@paulmillr
Copy link

👍 for #3

@curvedmark
Copy link
Author

#3 is good, but I'd like to add that there is another way to interprete the object:

a: b
c: d if e

The object is {a: b} if e is false, and is {a: b, c: d} if e is true. I know this seems to introduce a new syntax (and might not be feasible), but I simply want to point out that there is another way to interprete it. :)

@TrevorBurnham
Copy link
Collaborator

@gavin Right, but then what about the case

a: b
c: d if e
f: g

?

On Nov 20, 2011, at 9:51 PM, Gavin [email protected] wrote:

#3 is good. But I'd like to add that there is another way to interprete the object:

a: b
c: d if e

The object is {a: b} if e is false, and is {a: b, c: d} is e is true. I know this seems to introduce a new syntax (and might not be feasible), but I simply want to point out that there is another way to interprete it. :)


Reply to this email directly or view it on GitHub:
#1871 (comment)

@curvedmark
Copy link
Author

I guess {a: b, f: g} if e is false, and {a: b, c: d, f: g} if e is true. Basically a rough rule is, the expression (spans as long as reasonable) before if is effective if the condition is true.

@erisdev
Copy link

erisdev commented Nov 21, 2011

Huh. @gravof's suggestion is actually something I've often wished for when conditionally passing (as opposed to not passing) a particular keyword argument to a function. +0.7, but let's save it until after the issue at hand is resolved?

@jashkenas
Copy link
Owner

Apparently I'm in a special cases mood today, because I've opted for door number 2 -- special casing it so we get the intuitive behavior in both cases.

The rule is this: IMPLICIT_ENDs (postfix for, if, unless, etc.) will close implicit objects if they occur on the same line that the object was started on. If the implicit object continues onto multiple lines, only an explicit terminator or outdent will close it.

So:

call a: b if c

call 
  a: b
  c: x for x in list 

Means:

call(a: b) if c

call 
  a: b
  c: (x for x in list) 

@satyr
Copy link
Collaborator

satyr commented Dec 15, 2011

Shouldn't this compile too?

$ bin/coffee -bce '{a: b if x, d: e if y}'
({
  a: x ? b : void 0,
  d: y ? e : void 0
});

$ bin/coffee -bce 'a: b if x, d: e if y'
Error: Parse error on line 1: Unexpected ','
...

@michaelficarra
Copy link
Collaborator

I think we should reconsider this inconsistency. If I have a program

b: c if d

and I use it as an operand of a lower-precedence operator, =, the precedence of the postfix condition seems to jump down:

a = b: c if d

The problem is that the first example is the only place where postfix conditions/loops have such a high precedence. The claimed use case

fn
  a: b
  c: d if e
  f: g

is not convincing, and I don't think the inconsistency is worth it here. The user could easily parenthesise the value if that's what they intended. This would protect them from later edits changing their program in unexpected ways.

@vendethiel
Copy link
Collaborator

While I agree I havn't used the b: c if a form (AFAIR), I'm also wary of this change breaking the world without a clear pattern to search for in your codebase (^.+?:.+?if.+?$ ?).

@michaelficarra
Copy link
Collaborator

Yes, it would have to be considered a breaking change, which we'd only make in a major version bump. I don't think this pattern is very common, and I'd be okay making it in CSR if it was approved by the community here first. CSR currently tries but fails to completely support this inconsistency. It would be much simpler, actually, to support the consistent precedence.

@jashkenas
Copy link
Owner

I'm not perfectly clear on exactly what change you're suggesting here. Mind spelling it out a little bit more? Just removing the patch that closed this ticket?

@michaelficarra
Copy link
Collaborator

Yep, pretty much.

@jashkenas
Copy link
Owner

If anyone wants to take a crack at it, I think it would be illuminating to see what changes in the test cases in the PR, with the optimization removed.

@xixixao
Copy link
Contributor

xixixao commented Jan 23, 2014

+1 to fix this. I think that the patch hasn't considered the case of:

f = ->
  a: b if condition
# not consistent with
f = ->
  a if condition

and

f = ->
  a: b for i in c
#not consistent with
f = ->
  a for i in c

As far as I can tell the problem only arises in implicit returns, so we could, if we further dwelt in special-casing mood, fix that case and keep the current behavior. See other uses are working fine:

x = a: b if c
x = (a: b for i in c)
method a:b if c

Or we can just go with what Trevor proposed and others +1'd.

This would protect them from later edits changing their program in unexpected ways.

Can you show an example @michaelficarra ? Because I don't see an easy edit that would go from one compilation form to the other.

@vendethiel
Copy link
Collaborator

from

a = b: 1 if c
# to
a =
  a: 2
  b: 1 if c

I guess

@michaelficarra
Copy link
Collaborator

Exactly, @Nami-Doc.

@xixixao
Copy link
Contributor

xixixao commented Jan 23, 2014

Yeah, so with my suggestion this inconsistency stays:

a = b: 1 if c
# vs
a =
  b: 1 if c

But I am not entirely sold on this being such a big problem, because I would expect each to compile as they do. (Then again, I was never a fan of postfix conditionals in the first place.)

xixixao added a commit to xixixao/coffee-script that referenced this issue Jan 23, 2014
michaelficarra added a commit that referenced this issue Jan 23, 2014
Fixes #1871, close implicit objects in implicit returns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants