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