[parenscript-devel] Patch: added JS label/break to PS

Vladimir Sedach vsedach at gmail.com
Mon Nov 15 17:30:26 UTC 2010


> 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 <vsedach at gmail.com> 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 <danielgackle at gmail.com>:
>> > 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 <vsedach at gmail.com>
>> > 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 <danielgackle at gmail.com>:
>> >> > 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 <reddaly at gmail.com> 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 <vsedach at gmail.com>
>> >> >> 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 <danielgackle at gmail.com>:
>> >> >> >> 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
>> >> >> >> parenscript-devel at common-lisp.net
>> >> >> >> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >> >> >>
>> >> >> >>
>> >> >> >
>> >> >> > _______________________________________________
>> >> >> > parenscript-devel mailing list
>> >> >> > parenscript-devel at common-lisp.net
>> >> >> > http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >> >> >
>> >> >>
>> >> >> _______________________________________________
>> >> >> parenscript-devel mailing list
>> >> >> parenscript-devel at common-lisp.net
>> >> >> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >> >
>> >> >
>> >> > _______________________________________________
>> >> > parenscript-devel mailing list
>> >> > parenscript-devel at common-lisp.net
>> >> > http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >> >
>> >> >
>> >>
>> >> _______________________________________________
>> >> parenscript-devel mailing list
>> >> parenscript-devel at common-lisp.net
>> >> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >
>> >
>> > _______________________________________________
>> > parenscript-devel mailing list
>> > parenscript-devel at common-lisp.net
>> > http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >
>> >
>>
>> _______________________________________________
>> parenscript-devel mailing list
>> parenscript-devel at common-lisp.net
>> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>
>
> _______________________________________________
> parenscript-devel mailing list
> parenscript-devel at common-lisp.net
> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>
>




More information about the parenscript-devel mailing list