3 ms·
No, blocks are not a hack around first-class functions. Let’s talk about “what we’re making first-class.” A first-class function is something we both understand
by raganwald 15y ago
No, blocks are not a hack around first-class functions. Let’s talk about “what we’re making first-class.” A first-class function is something we both understand: A thingy with its own variable scope, its own notion of “this,” some parameters, and some executable code that may or may not return a result.
So what is a block? That’s easy in JavaScript. Here’s a block:
if (foo === bar)
// The block starts here v
{
return foo;
}
// The block ends here ^
Blocks are the chunks of executable code living inside of a function. They aren’t functions! They share their enclosing function’s scope. They share their enclosing function’s notion of “this”. You can’t “return” from a block, if you execute a return form a block, you return from the surrounding function.
Blocks already exist in JavaScript, but at the moment they only exist for built-in keyword construct like “if” and “for." Yahuda is simply explaining why it would be valuable to create a way to pass blocks to functions. The blocks would continue to be associated with their enclosing function invocation, unlike passing a function.
To summarize, JavaScript already has first-class functions, and it already has blocks, this proposal concerns a way to make blocks first-class. Blocks are not a hack around first-class functions, they’re something else that JavaScript already has.
- oinksoft 15y agoActually, while it's almost never used (I certainly haven't seen code using it), blocks cooperate with labels too. The following is perfectly valid: var fn = function() { nowWeDance: { dance(); } }; The block uses the same execution context and scope, of course, so it's not useful. A block is legal alone too: (function() { { return 1; } })(); // 1 I suppose for a very long switch statement (god forbid), blocks could be useful: switch (x) { case 1: { // do stuff break; } case 2: { // do stuff break; } } But I wouldn't favor it because it makes the break seem implicit to the reader when it in fact is not.
- deleted 15y ago[deleted]
- evanphx 15y agoYou can actually cause a block to return control to it's invoker (the equiv of return in JS) by using the 'next' keyword.
- gizmo 15y agoHe's not proposing to make blocks first-class! That would mean you'd be able to assign blocks to variables, and that makes little sense. Blocks would remain special non-first-class entities.
- raganwald 15y agoNo, he isn’t proposing that blocks be entirely first class. Although... Why not? foo ? bar.map { |x| x * x } : bar.map { |x| x + x } Could be: bar.map foo ? { |x| x * x } : { |x| x + x } Perhaps this is too much or too Ruby-ish: function either (pred) { return pred ? { |x| x * x } : { |x| x + x }; } bar.map(either(foo))
- gizmo 15y agoConsider the following: function map(a, cc) { r = [] a.each{|e| r.push( cc(e) ) } return r } Now `cc` is some kind of callable. It might be a function or it might be a block. Suppose `cc` is a block defined as cc = {|e| return e } Does this mean the `return` in `cc` breaks out of the outer each-loop because it's a block as well, or does the return simply do the normal thing? In the first case you need case analysis every time you write a higher order function in order to make sure the code flow makes sense. This is unacceptable. The alternative is that the "return e" in `cc` behaves like it would in a lambda function (i.e. the map function works correctly). But that means that when you assign a block to a variable you change its semantic meaning. Also completely unacceptable. I see absolutely no way to make first-order blocks work.
- raganwald 15y agoIn the spirit of brainstorming, I think it’s cool to come up with some cases and ask, “what if?” In Yehuda’s article, he gives: withFile: function(block) { try { var f = File.open(this.name, "r"); block(f); } finally { f.close(); } } This is interesting: “block” appears to be a “callable” object. How does the code know whether it will be a block or a function? Can you assign the block parameter to a variable? Pass it to another function? What about: function firstClass (block) { return block; } Can I do this? If so: var thisIsaBlock = firstClass { |x| return x; } And we’re off the rails. Or worse: (function () { var local = ‘0xDEADBEEF'; return firstClass { || local } })()() If I am reading the code example correctly and my inference holds, then either (a) you have to do a lot of escape analysis to make this work, or (b) you get lazy and allow programmers to pass blocks around but emit an exception if you try to call a block once its lambda environment has already returned. I guess I need to read the whole proposal... I am sure this has been addressed.