Closed satyr closed 13 years ago
s/ consensus / Stan's view /
We should support it--"How do you create a function and call it immediately?" is one of the most frequent questions, and I hate to say "You have to use parens."
Alright, it's the same deal with function binding. We have an example of a custom Function::bind
in the FAQ, we can do the same with scoping.
While the using parens is a minor annoyance, most of the proposals discussed here seem even more convoluted, especially those doing odd wrangling of variables.
So a -1 from me at the moment.
I thought we had a near-consensus around adding do
, and just weren't 100% there on the argument-passing syntax. Here's a concrete proposal (paired lines are equivalent):
do func # for consistency's sake
func()
do -> f()
(-> f())()
do (x = y) -> x()
((x) -> x())(y)
do (x) -> x() # shorthand for `do (x = x) -> x()`
((x) -> x())(x)
And of course do =>
would work the same as do ->
, except that the function is called in the current context.
I for one would find this to be a very useful feature. Even in the trivial examples given above, the do
lines read much more cleanly than the old-style self-executing closures. Big +1.
do (x = y) -> x()
Doesn't make sense with current syntax. Maybe we should have default arguments, eventually.
Yes, I was going to resurrect default arguments as a separate issue after this one was resolved... but since you find the syntax jarring (since (x = y) ->
wouldn't compile without the do
in front of it), I'll go ahead and raise it now.
[Update: It's issue 802.]
OK something that's been bothering me -- if we killed splices/slices using [..]
as they can be replaced with .slice
instead, why do we want a do
feature that can be implemented as a one-liner? See the inconsistency?
if we killed splices/slices using
[..]
as they can be replaced with.slice
instead, why do we want ado
feature that can be implemented as a one-liner?
- This issue is for an aesthetic improvement similar to the braceless object (all it does is to let you omit
{}
). "Calling bare functions without parenthesizing them" isn't possible without this proposal.- "Having to declare a function to achieve it" is a clear sign of a feature needed. Sp?lice was not.
- Sp?lice was a sugar that did less than its nonsugared equivalent.
do
is not.
I would add that there are significant performance gains to be had from using the mess of parentheses that do
compiles to, rather than writing your own equivalent function:
http://jsperf.com/self-executing-closure-vs-run-function
Though, there is a tradeoff in code size: r(...)
takes fewer bytes than function{...}()
. Still, the fact that I've seen self-executing closures in production code many times, and never seen a "run closure" function in production code, is telling.
Besides, do (x = 1, y = 2) -> ...
reads much, much more clearly than run (((x, y) -> ...), 1, 2)
, wouldn't you say?
< offtopic >
Testing JavaScript performance and having reliable results is a myth... I can never get accurate results with JSPerf. I am on Chrome 9 and my tests are against everyone's expected results. 48 / 42 in favour of Run. Go figure.
< /offtopic >
As the discussion is moving along I am starting to like do
if it is kept simple.
asyncEval: (next) ->
do next
Looks pretty.
I'm pretty sure I am -1 on this.
do next
is far better written as next()
.
for cat in litter then do (cat) -> cat.age++
is better written / more per-formant as
incrAge = (cat) -> cat.age++
for cat in litter then incrAge(cat)
I'm not sure if the cases where do
is useful are common enough for it to be considered.
is better written / more per-formant as
Thanks for the bench, Tim. That brings us back to the magical rescoping issue; why it was bad, what we really need and how we can do for it.
So how about this:
# when we see a directly called function without parameters under `for`
for cat in litter then (-> cat.age++)()
↓
# perform this conversion for good
_fn = (cat) -> cat.age++
_fn cat for cat in litter
And of course, the calling part is better written as do -> cat.age++
;)
@Tim-Smart While the usefulness of self-executing closures is debatable (they're mainly about keeping programmers from doing stupid things by keeping their variables isolated), they're common enough that people complain pretty often about the ugliness of having to wrap them in parentheses in CoffeeScript.
Also, there was a lengthy issue before discussing implementing some alternative means of calling functions without arguments without using parentheses. There are cases where such a syntax could greatly increase readability (to non-Lispers). While do next
isn't such a case, contrast these:
if (trim(getName()) is trim(ownerName())) and open then welcome()
if (trim(do getName) is trim(do ownerName)) and open then welcome()
I find the reduction in nested parentheses to be a breath of fresh air.
do
very useful in loops. So i recommend apply next:
do -> continue
→
(function(){ return; })()
do->break
→
var __break={}; try{ (function(){ throw __break; })() }catch(__E){ if(__E===__break)break; else throw __E; }
do->return val
→
var __return=function(__arg){this.value=__arg;}; try{ (function(){ throw new __return(val); })() }catch(__E){ if(__E instanceof __return)return __E.value; else throw __E; }
Since discussion on this ticket has settled down, I'm closing it as a wontfix
-- here's why:
do
is never more efficient than creating a nicely-named local variable to hold the function.do
is easily replaced by a helper function, without having to add anything to core.do
interacts strangely with =>
and ->
. do
to justify taking a keyword, it would need to be meaningfully better or more compact than the alternatives, and it doesn't pass that hurdle.Here's how it is more compact in my case. I like having the ability to close some variables without having to use the more verbose method, or naming a function I don't need named.
do (x, y) -> becomes ((x, y) -> ...)(x, y)
# this?
for report in array(doc)
((report) ->
sql.select 'order', { account_id: account.id, source_id: report.id['#'] }, true, (order) ->
if order?
update_order report, order
else
sql.insert 'order', { account_id: account.id, source_id: report.id['#'] }, (order) ->
update_order report, order
)(report)
# or this
for report in array(doc)
do (report) ->
sql.select 'order', { account_id: account.id, source_id: report.id['#'] }, true, (order) ->
if order?
update_order report, order
else
sql.insert 'order', { account_id: account.id, source_id: report.id['#'] }, (order) ->
update_order report, order
Ah, I was just bitten by this. Since we removed the magic from generated closures on master I had to go back and change my code in quite a few places. This is jut ugly:
fs.stat item = path.join(directory, file), ((item) -> (error, stat) ->
throw error if error
if stat.isDirectory()
scan [item], block
else
process.nextTick -> block item
)(item)
I mostly agree with Jeremy, apart from:
do
is never more efficient than creating a nicely-named local variable to hold the function.
I like my code to be linear. If I define functions before I use them this breaks my rule.
naturalethic I like having the ability to close some variables without having to use the more verbose method, or naming a function I don't need named.
StanAngeloff This is jut ugly: ...
I've implemented the hack proposed in my last post on my branch. See it in action here.
FYI -- the magic to generate closures inside of loops is back on master.
FYI, again -- the magic to generate closures is back off master, for once and for all, and I've added do
.
We all know the usefulness of this idiom, but the ugliness of the parens is intolerable.
new ->
works sometimes, but is flawed.Would be really nice if we could find a way to pass arguments as well.