> Did implementing this change with the modifications you made around how > expressionize works? My original patch for block/return-from used > expressionize, perhaps somewhat extended. I took a peek at the patch but > did not review it in detail. Are you satisfied with the current approach to > how you worked this in, or did you encounter some idiosyncrasies?
Yeah, I basically had to throw out the idea of expressionize. It only really worked with RETURN because it depended on being able to return control from within certain places in a certain way. I tried to extend multiple-value-bind to handle that correctly, then it became apparent that to "expressionize" things the right way means different things for different combinations of special forms. Luckily it's not a full Cartesian product as the special operators can be grouped into a few common classes. But it gets kind of tricky because some combinations need to mix both transferring control and passing values. > Just trying to keep tabs on how the code evolved and contributors' attitudes > towards it Well, I know what the contributors' attitudes towards it are - "you f$%#^* changed everything and broke all of my code." The major thing I did that will cause merge problems for everyone is split up how special operators are defined: with the new interface, you're encouraged to explicitly specify different expansion code for how the operator is expanded when its parent form wants an expression, and when it wants a statement. What I want to do is take a look at everyone's Parenscript forks that add new features (so far those would be yours and 3b's, anyone else have a fork?) and look at their new patches and try to get all the interesting stuff into Parenscript mainline. I also suspect that with a little judicious inlining, some of the current run-time library functions can be implemented in Parenscript itself. Vladimir > > On Sun, Nov 14, 2010 at 4:44 PM, Vladimir Sedach <[email protected]> wrote: >> >> Dammit, I was counting on being the lazy one. >> >> There's 6 different situations that block and return-from can be involved >> in: >> >> 1. implicit nil block in iteration forms (do/dolist etc.) in lexical >> extent: >> >> (ps (dolist (x '(1 2 3)) (when (= x 1) (return)))) >> >> => >> >> for (var x = null, _js_arrvar2 = [1, 2, 3], _js_idx1 = 0; _js_idx1 < >> _js_arrvar2.length; _js_idx1 += 1) { >> x = _js_arrvar2[_js_idx1]; >> if (x === 1) { >> break; >> }; >> }; >> >> 2. explicit nil or named block in lexical extent (we have to assign a >> name to the nil block): >> >> (ps (block nil (return) (+ 1 2))) >> >> => >> >> nilblock: { >> break nilblock; >> 1 + 2; >> }; >> >> 3. implicit named block established by defun, flet, and labels with >> lexical extent: >> >> (defun foo () (return-from foo)) >> >> => >> >> function foo() { >> return null; >> }; >> >> 4. implicit named block established by defun, flet, and labels with >> dynamic extent: >> >> (defun foo () ((lambda () (return-from foo)))) >> >> => >> >> function foo() { >> try { >> return (function () { >> throw { 'ps-block-tag' : 'foo', 'ps-return-value' : null }; >> })(); >> } catch (err) { >> if (err && 'foo' === err['ps-block-tag']) { >> err['ps-return-value']; >> } else { >> throw err; >> }; >> }; >> }; >> >> 5. explicit named block with dynamic extent: >> >> (block nil ((lambda () (return))) (+ 1 2)) >> >> => >> >> nilblock: { >> try { >> (function () { >> throw { 'ps-block-tag' : 'nilblock', 'ps-return-value' : null >> }; >> })(); >> 1 + 2; >> } catch (err) { >> if (err && 'nilblock' === err['ps-block-tag']) { >> err['ps-return-value']; >> } else { >> throw err; >> }; >> }; >> }; >> >> 6. implicit nil block in iteration forms with dynamic extent return >> >> (ps (dolist (x '(1 2 3)) ((lambda (x) (when (= x 1) (return))) x))) >> >> => >> >> Which is currently not implemented >> >> Vladimir >> >> 2010/11/13 Daniel Gackle <[email protected]>: >> > Sorry for being lazy, but can you post an example or two? This is a >> > feature >> > I will definitely try out. One of the unwanted weaknesses of my code on >> > the >> > JS side is the inability to get out of a top level function from inside >> > a >> > lambda. >> > >> > On Sat, Nov 13, 2010 at 2:11 PM, Vladimir Sedach <[email protected]> >> > wrote: >> >> >> >> 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 >> > >> > >> > _______________________________________________ >> > 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
