I just pushed a patch that tries to do the right thing with both lexical and dynamic-extent BLOCK (including implicit BLOCK forms) and RETURN-FROM. It's also supposed to provide backwards-compatibility with the old-style RETURN behavior (although that does issue a warning).
The big thing is that right now in most of the interesting cases it does the control jump, but does not return a value. That will be fixed in future patches. I haven't really tested it, so try it out and let me know what breaks. Vladimir 2010/8/18 Daniel Gackle <[email protected]>: > I like your suggestion of emitting TRY/CATCH only in the cases where > it's necessary, i.e. only when trying to escape out of more than one > level of function nesting, seems like a good way to go. Then you're > only paying for the ugliness when you need it. It's in keeping with > PS's philosophy of staying close to what one would write by hand. > > On Wed, Aug 18, 2010 at 6:12 AM, Red Daly <[email protected]> wrote: >> >> I added RETURN-FROM and BLOCK without too much effort using the >> implicit return functionality and try/catch. In my view this is the >> most reasonable way to implement this in the general case, since >> BLOCK/RETURN-FROM require non-local exit much in the same way that >> lisp's TRY/CATCH do. >> >> The alternative to this approach is to exit from each function in the >> call stack via a Javascript `return' statement. Unfortunately, the >> call stack can contain many functions code over which the Parenscript >> compiler exerts little control, requiring throw as the control >> transfer mechanism. Thus, in the general case of unknown code on the >> call stack, there is no means to exit without a throw. I do not view >> throwing as an ugly solution at all, since try/catch was designed for >> non-local exits of all sorts. >> >> Nonetheless, using try/catch to implement Parenscript features >> deserves some attention. Programs will need to ensure that they do >> not use try/catch in a way that interferes with the Parenscript >> convention. Generally, try/catch blocks should only catch specific >> exceptions and re-throw PS's exceptions. I'm happy to also implement >> a safe TRY/CATCH wrapper that re-throws Parenscript errors and catches >> everything else, too. However, we may want to make an official >> interface change to try/catch if any lisp-style non-local exit code >> becomes part of the language. >> >> I present an example of why try/catch is unavoidable inline below: >> >> On Fri, Apr 9, 2010 at 3:42 PM, Vladimir Sedach <[email protected]> wrote: >> > Makes sense to me. I'll add this to my todo list (which I'll publish >> > in an email as soon as I'm done my current work on the PS compiler). >> > >> > Vladimir >> > >> > 2010/4/9 Daniel Gackle <[email protected]>: >> >> I just pushed a patch (authored by Scott) to implement JS's LABEL and >> >> BREAK >> >> in PS. (Note that this patch deprecates LABELED-FOR since you can get >> >> the >> >> same effect by combining LABEL and FOR. Was anybody using LABELED-FOR?) >> >> Here's an example: >> >> (label scope >> >> (foo) >> >> (when (bar) >> >> (break scope)) >> >> (blee)) >> >> => >> >> scope: { >> >> foo(); >> >> if (bar()) { >> >> break scope; >> >> }; >> >> blee(); >> >> }; >> >> I was astonished to discover recently that JS has supported this >> >> ability all >> >> along in the form of labeled statements and labeled breaks. I'd always >> >> assumed that to get explicit returns from an arbitrary scope, you'd >> >> have to >> >> resort to the ugly hack of muscling TRY/CATCH to do it, thinking that >> >> this >> >> was the closest JS counterpart. >> >> (See http://news.ycombinator.com/item?id=793092 for a thread in which >> >> several people believe this.) But it appears we were all wrong. >> >> What's not clear yet is how far this can be taken. Can you use it >> >> inside a >> >> nested local function to return immediately from the top-level >> >> function? >> >> That is one thing I've wanted for a long time. >> >> In the ideal case, LABEL/BREAK could be used as a base for implementing >> >> a >> >> proper BLOCK and RETURN-FROM in PS, something which we'd long believed >> >> to be >> >> impossible. One challenge is that in CL, RETURN-FROM can take a value, >> >> which >> >> becomes the value of BLOCK. In other words, BLOCK in CL is an >> >> expression >> >> while LABEL in JS is not. It seems, though, that most of this challenge >> >> has >> >> already been conquered with the development of implicit return in PS. >> >> The >> >> only thing you'd need to add is detecting when BLOCK is being used as >> >> an >> >> expression, declaring a gensymed variable and assigning whatever is >> >> happening inside BLOCK to that variable (much like implicit return >> >> already >> >> does with e.g. CASE), then put the variable in the expression position >> >> that >> >> BLOCK was in. It seems like this ought to work. It would also make >> >> things >> >> like this possible in PS: >> >> (1+ (case foo >> >> (:eleven 11) >> >> (:twelve 12))) >> >> Vladimir (and everybody), is the above clear? What do you think of it? >> >> As stated above, I think try/catch is the way to go. There is no >> other way to exit a stack of functions in the general case otherwise. >> >> For example, I can write a Javascript function that calls its argument >> infinity times and never returns. >> >> function mapForever(fn) { >> while(true) fn(); >> } >> >> Now consider some parenscript: >> >> (block non-local >> (map-forever >> (lambda () >> (return-from non-local "we got out!")))) >> >> To extricate itself from map-forever, there is no alternative but JS's >> throw statement. >> >> Even if we had the ability to alter every function in the system, it >> would be necessary to inspect nearly every function call's return >> values to properly unwind the stack to the appropriate BLOCK. >> >> Having said all that, there are cases when try/catch is not necessary >> for BLOCK/RETURN-FROM, as you have described. BLOCK should emit code >> according to the contexts in which RETURN-FROM appears. If there is a >> RETURN-FROM inside the same function, BLOCK can use a label for a >> local exit. If RETURN-FROM appears inside a lambda, try/catch is >> necessary (except in cases where you want to optimize this away by >> inspecting how that lambda gets passed around). If there are no >> return-froms, just emit a PROGN. >> >> My solution does not do the local optimization, but it does refrain >> from putting try/catches around code with no return-froms. >> >> >> >> Red >> >> >> Daniel >> >> _______________________________________________ >> >> parenscript-devel mailing list >> >> [email protected] >> >> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel >> >> >> >> >> > >> > _______________________________________________ >> > parenscript-devel mailing list >> > [email protected] >> > http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel >> > >> >> _______________________________________________ >> parenscript-devel mailing list >> [email protected] >> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel > > > _______________________________________________ > parenscript-devel mailing list > [email protected] > http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel > > _______________________________________________ parenscript-devel mailing list [email protected] http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
