Skip to content

Documents do (x=1, y=2) -> syntax #2660

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

Closed
wants to merge 22 commits into from
Closed

Documents do (x=1, y=2) -> syntax #2660

wants to merge 22 commits into from

Conversation

raganwald
Copy link
Contributor

The existing documentation explains:

When using a JavaScript loop to generate functions, it's common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don't just share the final values. CoffeeScript provides the do keyword, which immediately invokes a passed function, forwarding any arguments.

Version 1.3.1 added the ability to use do to protect variables from being hoisted:

x = 0
do (x = 1, y = undefined) ->
  alert(x) # alerts '1'
alert(x) # alerts '0'

This commit documents this pattern under the variables and scoping section.

@goshacmd
Copy link

👍

@raganwald
Copy link
Contributor Author

Thanks, @icambron!

@eviltrout
Copy link

+1

@satyr
Copy link
Collaborator

satyr commented Jan 10, 2013

Sounds unjashkenasy to me. Has our BDFL ever accepted variable shadowing in general?

the do keyword creates a new lexical scope

Actually it's the FE that creates a new scope. do only invokes it, changing the parameters' behavior when directly applied:

$ coffee -bce 'do (x = y) ->'
// Generated by CoffeeScript 1.4.0

(function(x) {})(y);

$ coffee -bce 'do ((x = y) ->)'
// Generated by CoffeeScript 1.4.0

(function(x) {
  if (x == null) {
    x = y;
  }
})();

@raganwald
Copy link
Contributor Author

@satyr, yes of course it's the IIFE that does the job. When documenting this, are we to discuss the implementation or the abstraction?

Is the second example a bug in the way do is parsed? That is actually the way do semantics worked prior to version 1.3.1. The 1.3.1 change was specifically to support shadowing.

Finally, my understanding is that @jashkenas discourages the use of do in this manner for normal programming but provides it as a fix for when shadowing is necessary. It is better than the horrible workaround of

do ->
  `var x = y`
  # ...

That some people have espoused.

@michaelficarra
Copy link
Collaborator

I don't like it. I'd like to see some mention of its relation to a let-like construct. Also:

the do keyword creates a new lexical scope and accepts a comma-delimited list of variables and values

Not quite. You're trying to show off a common pattern of using these two features together. The do keyword simply invokes its operand, end of story. This special-cased combination of the two has the special shadowing behaviour.

you must provide a value, even if it's just undefined

...no? Where did that idea come from?

@satyr
Copy link
Collaborator

satyr commented Jan 10, 2013

are we to discuss the implementation or the abstraction?

Being accurate never hurt. I'd rather like to see a full explanation of do once for all. How about we officially introduce it first (e.g. do is a unary operator that calls the operand except blurblur), then merge yours and the other partial do documentation (index.html.erb L551)?

@icambron
Copy link

@michaelficarra > Where did that idea come from?

do obviously doesn't require you to assign all the values, but I think the point here is to explain how to shadow, and if you want to shadow some variable x to undefined, you do need to explicitly set it, since without a value at all it will set x to the enclosing scope's x, right?

@raganwald
Copy link
Contributor Author

If you do not assign a value, it transpiles this:

do (x) ->
  alert(x)

Into:

(function(x) {
  return alert(x);
})(x);

This is what you want when you are trying to generate functions and fix a value for x, as in a loop. It's equivalent to:

do (x = x) ->
  alert(x)

However, if there is no x defined in an outer scope, you get ReferenceError: Can't find variable: x, So, if you are trying to create a new scope and do not wish to deliberately use the value of x, the safest thing to do is write:

do (x = undefined) ->
  # ...

This transpiles to:

(function(x) {
  return alert(x);
})(void 0);

@raganwald
Copy link
Contributor Author

Long story short, there are three use cases:

  1. You want to generate a closure with the current value of x. use do (x) ->. This is documented under loops.
  2. You want to shadow x with a new value. Use do (x = value) ->. If you don't have a value for x and would use var x; in JS, use do (x = undefined) ->
  3. There is no enclosing x, but you're paranoid or trying to gratuitously emulate Harmony's let construct: use do (x = value|undefined) ->

The third case is considered un-coffeescript-y.

@satyr
Copy link
Collaborator

satyr commented Jan 10, 2013

use do (x = undefined) ->

Or new (x) ->.

@raganwald
Copy link
Contributor Author

new (x) ->

Elegant, and works well when programming in strict style (obviously, the semantics for this|@ differ). Are you suggesting that we simply explain the do (x = y) -> case and say nothing about how to shadow x when the code within the do sets the value later? or suggesting that we explain do (x = y) -> and also explain new (x) ->?

@raganwald
Copy link
Contributor Author

I'd rather like to see a full explanation of do once for all. How about we officially introduce it first (e.g. do is a unary operator that calls the operand except blurblur), then merge yours and the other partial do documentation (index.html.erb L551)?

I like this too and I'm happy to do it. Would it be helpful to submit a separate issue and pull request? Or should we try to arrive at a consensus here and I'll update the docs here?

@satyr
Copy link
Collaborator

satyr commented Jan 10, 2013

also explain new (x) ->?

Kind of redundant since it's a normal JS idiom (and the doc assumes fluent JSers), but mentioning it doesn't hurt I guess.

Would it be helpful to submit a separate issue and pull request? Or should we try to arrive at a consensus here and I'll update the docs here?

Former perhaps to reset noise.

@michaelficarra
Copy link
Collaborator

I'd prefer it all stay here. Push new commits, we'll squash when we merge it. That way we have the entire conversation in one place.

@raganwald
Copy link
Contributor Author

Ok, to summarize what I'm going to do:

  1. Add a "do" section and merge the two separate behaviours.
  2. Leave out the do (x = undefined) ->, people can discover this through blog posts, books, whatever, if it is to their taste.
  3. Leave out the new (x) ->, it's a useful idiom but not a CS-feature.
  4. Be more explicit about do being sugar for an IIFE.

Agreed?

@michaelficarra
Copy link
Collaborator

@raganwald: That sounds good to me.

@satyr
Copy link
Collaborator

satyr commented Jan 10, 2013

Be more explicit about do being sugar for an IIFE

Sugar for an invocation (()) to be precise. See #788 for its origin.

@raganwald
Copy link
Contributor Author

Yes, sugar for an invocation with specific rules about how it assigns values to the arguments.

@jashkenas
Copy link
Owner

I'm missing the do.coffee example in this pull request, I think -- and it would be great to amend it without any of the regenerated documentation files.

@vendethiel
Copy link
Collaborator

It's not missing, it's just that we already have it. The paragraph was only moved.

@ghost
Copy link

ghost commented Feb 1, 2013

Question: Does this pull request need any further action from me? @jashkenas? @Nami-Doc?

@vendethiel
Copy link
Collaborator

I don't have any influence on this, sorry :p.

@jashkenas
Copy link
Owner

Yep, it does -- can you amend it to remove the regenerated example bits of code? We'll do that part when we tag a release.

@jashkenas
Copy link
Owner

Sorry about this -- my delay has caused this PR to get woefully out of date, and GitHub is currently showing all kinds of crazy unrelated stuff in the merge diff. Closing, but feel free to resubmit a fresh copy.

@jashkenas jashkenas closed this Mar 4, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.