-
-
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] | |
… |
@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.
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.