Skip to content

Instantly share code, notes, and snippets.

@kriskowal
Created January 28, 2012 00:28
Show Gist options
  • Save kriskowal/1691779 to your computer and use it in GitHub Desktop.
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
Copy link

dherman commented Jan 29, 2012

If my function is going to be interrupted, I expect to be able to observe that in a catch or finally clause and clean up resources.

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 to return unless you hand them a capability to return in the form of a block that closes over return.

@dherman
Copy link

dherman commented Jan 29, 2012

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 by foo, 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.

@kriskowal
Copy link
Author

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

@kriskowal
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment