-
-
Save kriskowal/1691779 to your computer and use it in GitHub Desktop.
function foo() { | |
bar({|x| | |
return 10; | |
}); | |
} | |
function bar(block) { | |
block(); | |
block(); // we should never get here. how? | |
} | |
foo(); |
10 foo(); // stack: [] | |
2 bar({|x| return 10}) // stack: [foo] | |
7 block() // stack: [foo, bar] | |
3 return 10 // stack: [foo, bar, block] | |
… |
Perhaps the “exception way of thinking” should be more literally true. A thrown value, perhaps a
ReturnValue
exception like that codified by your generators improvement proposal, implicitly caught byfoo
, identified by its object identity as being associated with itself, and converted into a literal return value.
This approach would be dynamic scoping, which has two problems. 1) Any function you call would implicitly have the ability to force you to return; 2) It breaks abstractions; if you pass a block lambda that does a return
to another higher-order function, it will accidentally return from that function instead of the function it appeared to be lexically bound to.
@dherman Right. The finally
clause might be enough.
I did not mean to imply that you could fabricate a ReturnValue
exception and throw it, causing a return from the first parent scope. I mean that a return
in a block lambda would construct a ReturnValue
exception with a read-only value
(at least), add it to a side-table.
var sideTable = new WeakMap();
// return value desugars
var exception = new ReturnValue(value);
sideTable.set(exception, foo);
throw exception;
// foo desugars
function foo() {
try {
} catch (x) {
if (sideTable.get(x) === foo) {
return x.value;
} else {
throw x;
}
}
}
Then foo
would implicitly catch the exception and convert a ReturnValue.value
into a return value only if the exception in the side-table maps to foo
.
This would make it possible for an intermediary to observe the exception in a catch block, rethrow it, or throw a different exception. It would not give the ability to return an arbitrary value from an arbitrary parent scope.
@dherman I think that finally
will have to suffice. It would be an information/capability leak for an intermediate stack frame to be able to observe the return value in a parent lexical scope. MarkM would kill me for suggesting it.
Well first of all, you can use a
finally
block to clean up resources. That wouldn't change.As for
catch
, the difference is that nobody but lexically enclosed code (i.e., you) can fabricate the ability to cause that local exit unless you explicitly pass them a block that you wrote yourself. That is, unlike exceptions, nobody has the ability to cause you toreturn
unless you hand them a capability toreturn
in the form of a block that closes overreturn
.