JavaScript Needs Blocks
yehudakatz.com
yehudakatz.com
> There are two very common problems here. First, this has changed contexts. We can fix this by allowing a binding as a second parameter, but it means that we need to make sure that every time we refactor to a lambda we make sure to accept a binding parameter and pass it in. The var self = this pattern emerged in JavaScript primarily because of the lack of correspondence.
Or, you could use Function.prototype.bind where it's needed. Only functions where an execution context is frequently provided should accept an execution context (as sugar, basically), and it's generally better to assume prudent use of bind(). But I guess I'm crazy.
He then proposes a wholesale change to function semantics in the language. There are no "acrobatics" performed if you want to return a value passed to forEach(). You just enclose a variable and assign from within the callback. Any experienced JS dev will tell you that the disadvantage of forEach() and relatives is not that you can't easily return, but rather, that you can't easily break. However, proponents of a functional style would argue that you should be filtering your list first so that you only have to deal with interesting values, and so that there is no need to break: If you need `break`, use for ()!
I mean, come on, this is the same guy who wrote a reopenClass function in Ember.js -- for a language with plainly open prototypes.
This is nothing but a post glorifying Ruby and bashing JS for not being Ruby. There are plenty of valid nits to pick with JS, but being unlike Ruby is not one of them.
Actually, I linked to a proposal by Brendan Eich, the creator of JavaScript.
I mean, come on, this is the same guy who wrote a reopenClass function in Ember.js
JavaScript's open prototypes suffer from the inability to define a number of new properties at once using an object literal. reopen (not reopenClass), provides that functionality.
This is nothing but a post glorifying Ruby and bashing JS for not being Ruby
Nope. It's a post glorifying the correspondence principle, which Smalltalk and Lisp had before Ruby, and arguing that JS would be better with it.
Naming names doesn't change the idea: It is surely a wholesale change to function semantics in the language.
> JavaScript's open prototypes suffer from the inability to define a number of new properties at once using an object literal. reopen (not reopenClass), provides that functionality.
Sure, that's why you use a general-purpose merge function, prototypes just being objects themselves.
var merge = function(o, o2, force) {
for (var p in o2)
if (o2.hasOwnProperty(p) && (!(p in o) || force))
o[p] = o2[p];
return o;
};
var C = function() { /* ... */ };
merge(C.prototype, {
foo: function() { /* ... */ },
bar: function() { /* ... */ }
});
In any case a name like reopenClass() sounds very much like an attempt to make JS smell like Ruby even if all the function does is perform a merge. I can't take Katz' suggestions for JavaScript seriously.
...
Naming names doesn't change the idea.
I’m confused! Are we judging ideas by the source or by their merits?To which you answered "Naming names doesn't change the idea."
But (!) writing
C.reopen({
foo: function() { /* ... */ },
bar: function() { /* ... */ }
})
instead of /*window.*/merge(C.prototype, {
foo: function() { /* ... */ },
bar: function() { /* ... */ }
})
makes for easier reading, which is worth a lot in my opinion.It's the same as Cocoa providing -[NSMutableArray removeLastObject] in addition to -[NSMutableArray removeObjectAtIndex:] and -[NSMutableArray length]
Sure you can express one with the other, but readability matters.
Oh, but it does. First, it shows that you didn't understood what you read (re: who wrote the proposal).
Second, it shows that the thing that you deemed as unfit for the language is considered as fit by the VERY CREATOR of said language.
"""It is surely a wholesale change to function semantics in the language."""
So what, if it's needed?
But Lisp doesn't need two different kinds of lambda (at least neither Common Lisp nor Scheme has anything like that).
I'd be interested to know exactly what about JavaScript forces it to need blocks to obey the CP, when Lisp doesn't need them - I haven't quite wrapped my head around that yet. Is it just the problems with 'return' and 'this'? Could that be fixed by instead adding Common Lisp's 'return-from', and fixing 'this' somehow, instead of by adding blocks?
> (defun foo ()
(block nil
(funcall (lambda () (return 3)))
4))
FOO
> (foo)
3
So the problem is that JavaScript's `function' always creates a return point.The solution proposed by OP (distinguishing "lambda" callables from "block" callables) is in my opinion misguided, because it only solves a problem partially, in a confusing way and creates new, unnecessary entities. The return-from mechanism is simpler and more intuitive.
People would like JavaScript to be more like Ruby, idiomatic Perl 6 resembles Ruby… Maybe we need a new law: «widely used programming languages are modified until they resemble Ruby». :)
This is an interesting observation, and it has echoes of Greenspun's Tenth Rule. I'm not sure exactly how true the statement is, but I suppose it used to be (or still is?) the case that the same thing could've been said with C in place of Ruby.
It should be noted that sufficiently popular languages incorporate as many buzzwords and design patterns as possible, which I suppose can be accounted for as 'features'.
(Excellent article, by the way).
I like it, but I stick with Perl because it adds the OO stuff with Moose and being an older project has made more mistakes that have been fixed. Oh and CPAN of course,
Still telling people to either learn Ruby, if they want to get up to speed quickly. Else I tell them to learn Perl. It's a bit harder to learn, but easier to use. Well for some people (linguists, etc.) Perl is easier to learn too.
He isn't proposing a change to how functions works at all. He is proposing the addition of blocks, which would not break any current functionality. And he isn't the one proposing it, he is endorsing Brendan Eich's proposal.
"This is nothing but a post glorifying Ruby and bashing JS for not being Ruby."
I don't think it is. He mentions that Javascript feels elegant for code with a lot of callbacks. He doesn't seem to be bashing js at all, just pointing out places where blocks lead to more straightforward refactorings than lambdas. Hammers vs. wrenches and all that good stuff. He's not asking that all the hammers be replaced with wrenches, he's just asking for the additional option to use wrenches when it would be better to use a wrench.
Blocks are a useful tool in the toolkit. Javascript is not a purely functional language so it's not like some functional purity would be violated. In fact, your proposed solution: "If you need `break`, use for ()!" is much farther from the functional mindset than blocks are.
Yes, Yehuda is a Rubyist and that colors his presentation. But let's rise above ad-hominem arguments. You invoke blub but your argument in defense of JS is pretty much blubby, where it doesn't miss something already in JS (some and every).
Shorter and/or better function or lambda syntax and semantics have been on the agenda for years. Just shortening 'function' is not enough. Various half-baked proposals having failed, last year I went through detailed design exercises for both CoffeeScript-inspired (Dart took some of that inspiration)
http://wiki.ecmascript.org/doku.php?id=strawman:arrow_functi...
and Ruby- (but really Smalltalk; also E)-inspired
http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda...
The latter won on many technical points, even though it's not yet in Harmony. It may make it.
Briefly, arrows require a new grammar validation formalism (not LR(1) with special rules for ASI and lookahead restrictions), arrows leave users confused about when to use fat-arrow, and @jashkenas vouches "arrows or curly braces, not both". Blocks win by requiring no new standard parsing algorithm for validation, and more: for refactoring, iteration with built-in control effects (break/continue/return), and guaranteed |this| inheritance -- all owing to TCP.
BTW, Rust has Ruby-like blocks now.
On the contrary, you seem to be acting like a blub programmer looking up the power continuum and failing to understand the importance of Tennent's correspondance principle. You fail to see the importance of function(){ ... }() being equivalent to ...
Meanwhile, I look at JavaScript and go "how can anyone get anything done when 'this' is a painful violation of correspondance?"
In case you want to get your google/wikipedia on, what Katz is talking about here is a "non-local return". Normal returns return from the immediately enclosing (i.e. local) lambda/fn/block/thing. Non-local ones unwind past multiple enclosing functions to cause an outer one to return.
In Smalltalk, the distinction is between methods and blocks. A return expression always returns from the enclosing method and will unwind past any blocks that the return expression is contained in.
In Common Lisp, I believe you name functions and then indicate the name of the enclosing function that you want to return from by doing `(return-from <fn name> 3)`.
I thought non-local returns were a bit impure and kind of a weird language novelty for a while. Recently, I added them to a hobby language of mine (Finch) and then wrote some code that uses them.
Holy crap are they awesome.
Being able to early return is, I think, one of the things that's really handy about imperative languages. I use it all the time in my code. But that cripples your ability to define your own flow-control-like structures that take functions.
For example, here's some code that sees if an array contains a given item using a for loop:
function contains(array, seek) {
for (var i = 0; i < array.length; i++) {
if (array[i] == seek) return true;
}
return false;
}
Let's say you don't like doing an explicit C-style loop and you want to make your own forEach() control-flow-like function: function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
callback(array[i]);
}
}
Now we try to refactor `contains` to use it: function contains(array, seek) {
forEach(array, function(item) {
if (item == seek) return true;
});
return false;
}
Crap, that doesn't work. There's no easy way to make `forEach()` stop early unless you go out of your way to make it support that by having the callback return some magic sentinel value. Or you do something hideous like throw a "return" exception.Non-local returns solve this neatly and end up being pretty delightful to use in practice.
// returns true/false
array.some(function(item){
item === seek
})
Your "magic sentinel value" would be a Boolean, which is exactly how `some` is implemented: function forEach(array, callback) {
for (var i = 0; i < array.length; i++) {
if (callback(array[i]) === true) break;
}
}The GP is creating a (toy) language and observes that non-local returns make some code easier/cleaner to write and that non-local returns are therefore not just a language wart, but something to carefully consider when designing a programming language.
No problemo in Ruby:
def map_unless_nested(arr)
arr.map {|v| return [] if v.is_a? Array; yield v}
end
map_unless_nested([2,3,4,5]) {|v| v+1 }
# => [3,4,5,6]
map_unless_nested([2,[3,4],5]) {|v| v+1 }
# => []
This starts looking ugly in JavaScript if we want to generalize "map". Either we can keep "map" clean and wrap our callback so it throws exceptions on array input: function map(arr, callback) {
var ret = [];
for (var i = 0; i < arr.length; i++) {
ret.push(callback(arr[i]));
}
return ret;
}
function map_unless_nested(arr, callback) {
try {
return map(arr, function(v) {
if (v instanceof Array) throw "nested!";
return callback(v);
});
} catch (e) {
if (e==="nested!") { return []; }
throw e;
}
}
map_unless_nested([2,3,4,5], function(v){ return v + 1; })
// => [3,4,5,6]
map_unless_nested([2,[3,4],5], function(v){ return v + 1; })
// => []
Or, if we want to keep the callback clean, we have to start fiddling with map, and then it's no longer really just map (it's map with halting parameters), and then you're using special return values to inform calling functions that the halting condition was met. function map_unless_test(arr, callback, test) {
var ret = [];
for (var i = 0; i < arr.length; i++) {
if (test(arr[i])) { return false; }
ret.push(callback(arr[i]));
}
return ret;
}
function map_unless_nested(arr, callback) {
return map_unless_test(arr, callback, function(v) {
return v instanceof Array;
}) || [];
}
If you still think that exceptions are the best language feature to solve this example, consider the situation where you want to do something with those nested arrays. For example, let's take the Ruby example and modify it so it will return a copy of the first-encountered innermost array with the callback applied. def map_first_innermost(arr, &block)
arr.map do |v|
return map_first_innermost(v, &block) if v.is_a? Array
yield v
end
end
That was easy. You can see that non-local returns become useful very quickly. function map_unless_nested(arr, cb){
var res = []
var has_nested = arr.some(function(item){
if (item instanceof Array) return true
res.push(cb(item))
})
return has_nested ? [] : res
}
function map_first_innermost(arr, cb){
var res = []
var has_nested = arr.some(function(item){
if (item instanceof Array) return true
res.push(cb(item))
})
return has_nested
? map_first_innermost(res[res.length-1], cb)
: res
}That's not an outlandish requirement; imagine that the callback can be expensive, and we would like to be able to adjust a centralized "map" function later so it farms things out to different processes/workers/etc.
To solve this for map_first_innermost, you'd have to throw an exception with the nested array, but this is just getting hideously ugly.
function map_first_innermost(arr, callback) {
try {
return map(arr, function(v) {
if (v instanceof Array) throw {itWasNested: v};
return callback(v);
});
} catch (e) {
if (e.itWasNested) {
return map_first_innermost(e.itWasNested, callback);
}
throw e;
}
}It sounds like you think my requirements are contrived. If you prefer to use functions as iterators in JavaScript, and who doesn't (unless you like polluting methods with counter variables?) ... there is no way to have iterators halt early without throwing exceptions or coming up with special return values. Every programmer needs to iterate and break out of loops. It is easy to break from iterator functions in Python and Ruby, since the languages natively support this concept. JavaScript doesn't--that's all we're saying, and it would be nice.
function map_unless_nested(arr){
return arr.filter(function(it){ if (it instanceof Array) return it }).length?
function(){ return [] } : function(fn){ return arr.map(fn) };
}
console.log(map_unless_nested([1,2,3])(function(n){ return n + 1 }));
console.log(map_unless_nested([1,2,[1,2],3])(function(n){ return n + 1 }));
edit: also a translation for the last example function map_first_innermost(arr){
return function(fn){
return arr.map(function(v){
if (v instanceof Array)
return map_first_innermost(v)(fn);
return fn(v);
});
};
}
console.log(map_first_innermost([1,2,[1,2],3])(function(n){ return n + 1 })); function callcc(f){
f(function(x){ return x })
}Exactly right. That's the one nasty corner case I know of. I don't know how often it comes up in practice. I believe Ruby handles it by trying to make blocks not-very-first-class so that it's hard to capture one and have its extent outlast the outer stack frames.
class BlockInvoker
def initialize(&block)
@block = block
end
def invoke
@block.call
end
end
BlockInvoker.new { puts 1 }.invoke
def get_block_invoker
BlockInvoker.new { return }
end
get_block_invoker.invoke
I tried to cover this a bit in my post. For callbacks, this semantic is clunkier.function getMyData(input) { asyncDataCall(input, {|data| return data; // fail }); }
I do have a question regarding early returning the outer function scope, what happens if in your forEach definition, you actually do work on some (or all) elements that needs to be undone. If the outer forEach function is returned early by the anonymous function, how will any of the clean up code get called?
And not surprisingly some mad nutter has already implemented it in Perl - https://metacpan.org/module/Scope::Escape::Sugar
(I got a hold of a copy after I ran into the topic previously: http://blog.marcchung.com/2009/02/18/how-closures-behave-in-... )
The binding of this is solved with => (fat arrow), and the returning value is resolved with the fact that both the inner function and outer function will return their result (I think? :\) and thus the refactoring is essentially equivalent to the ruby version?
Anyone else read this as "Ruby guy wants javascript to be more like ruby." ?
You can absolutely download the source to Google's v8 javascript engine (http://code.google.com/p/v8/ ), or the implementations that Mozilla has built over the years (https://developer.mozilla.org/en/SpiderMonkey and http://www.mozilla.org/rhino/ ).
EDIT: Yeah check the comment below regarding tracemonkey rather than spidermonkey.
It should also be noted that Javascript is a specification first and foremost (unlike Ruby which is defined primarily by its reference implementation). It's worth reading through the standard: http://www.ecma-international.org/publications/standards/Ecm...
The actual complaint seems to be that JavaScript's scoping rules are different to his expectations and 'this' binding is, well, we know.
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.
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. 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)) 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.
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.
Not in general. In Ruby the case is more complex because Ruby blows, but if you take Smalltalk's blocks they implement closures (in Dolphin, they're instances of `BlockClosure`) and they're first-class functions.
The way in which colloquial "blocks" differ from "lambdas" first-class functions is control flow in the semantics of return: lambdas use local returns, a `return` will break from the current dynamic scope (discarding the current stack frame), whereas blocks use non-local returns: a `return` will break from an enclosing lexical scope (generally a method and you can't nest methods).
You could very well have both behaviors with the same function type, using different return operators (yehuda is full of crap when he asserts that you need both lambdas and blocks by the way, even more so since Smalltalk did not have lambdas: Smalltalk has methods and blocks, methods are not lambdas as they can't be anonymous or nested, they're a top-level construct of the class) just as you can already emulate non-local returns via exceptions today (that's basically what a non-local return is)
It's most useful in getting rid of "C-style blocks" constructs (by which I mean non first-class blocks), which Smalltalk did quite beautifully.
In order to have a language with return (and possibly super and other similar keywords) that satisfies the correspondence principle, the language must, like Ruby and Smalltalk before it, have a function lambda and a block lambda.
In the context of my article, "function lambda" means function in JavaScript and method in Ruby and Smalltalk. My argument is that you need a construct you can return from, and a separate construct (a "block") that can be nested in that construct.
And that's not true, you can have a single construct and two different returns (one local and one non-local) instead.
In a Lisp-like language with all of those features, you could define arbitrary non-local return points by defining a macro which uses dynamic-scoping to introduce a `return` function which captures the current continuation. One argument would be a lambda/closure that would be evaluated in this new scope: capable of calling `return`.
Edit: Also, as I think about it, one could maybe argue that closures are to expressions as blocks are to statements. In which case, it seems that blocks should never be bound to values but things that invoke blocks should only be able to do so as in the Ruby 'yield' where the block is implicit. You could then avoid the whole escaping problem.
Edit: And further, a thing which invokes a block should maybe belong to yet another category from closures and blocks? In a sense, such a thing is analogous to:
for (...)
or if (...)
In other words, such a thing is half-a-statement. You have to append a block to make a whole statement. :) // constructor for the FileInfo class
FileInfo = function(filename) {
function withFile(block) {
var f;
try {
f = File.open(filename, "r");
return block(f);
} finally {
f.close();
}
}
this.mtime = function () {
var mtime = withFile(File.mtime);
if (mtime < new Date() - 1000) {
return "too old";
}
sys.print(mtime);
};
this.body = function () {
return withFile(function (f) { return f.read(); });
};
};
An advantage with this refactoring is that the file handle, which isn't required after getting the mtime, is closed before doing other things.-1 For adding block syntax.
var new-return-from-keyword = "test";[EDIT]
For example you could teach functions to beginner programmers once. And could then easily introduce lambdas/callbacks/generators(And maybe block lambdas) without introducing much new syntax.
mtime: function () {
return reset {
var mtime = shift (succeed) {
this.withFile ({ |f|
var mtime = this.mtimeFor (f);
if (mtime < new Date () - 1000) {
return "too old";
}
return succeed (mtime);
});
};
sys.print (mtime);
return "young enough";
};
},
The succeed function is a first class, indefinite-extent function equivalent to: function (mtime) {
sys.print (mtime);
return "young enough";
}
It's the computation within the reset block that comes after the shift form is evaluated.Calling it after returning from the method would not raise an exception.
Edit: Keep blocks with 'this' inheritance (I had originally used a standard closure).
While I'm thinking about it... I wonder about the choice to overload/reuse the keyword 'return' in the blocks proposal. Any thought on using something else to emphasize the distinction in semantics?
Edit: Nevermind. That would break the Tennent Correspondence Principle wouldn't it? Bad idea.
class FileInfo
constructor: (@name)->
mtime: ->
#implicit return
@withFile (f)->
if f.time isnt 999 then "too old"
withFile: (block)->
try
block
name: @name
time: 1000
finally
console.log 'finished'
f = new FileInfo('name')
console.log f.mtime()
#finished#too old
http://mail.python.org/pipermail/python-3000/2006-November/0...
EDIT: of course the replies are correct. i suppose it was unclear what the parent meant by "anonymous functions". if he meant lambdas, then they are indeed closures but without the same capabilities as normal python functions. their capabilities are unrelated to the fact that they're anonymous or closures.
Python has closures.
Lambdas are anonymous functions which support only expressions, like all functions they can form closures.
Named functions also form closures and can contain any number of statements or expressions.
This is also known as the principle of abstraction, because it means that it is easy to refactor common code into methods that take a block.
I can attest that quickly refactoring code in Ruby is much more straightforward than in JavaScript. In practice, a small reduction in cognitive load means that it's easy to just do it now, instead of putting it off (to the point it never gets done).