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.
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@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@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@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@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@common-lisp.net
>> >> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >>
>> >>
>> >
>> > _______________________________________________
>> > parenscript-devel mailing list
>> > parenscript-devel@common-lisp.net
>> > http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>> >
>>
>> _______________________________________________
>> parenscript-devel mailing list
>> parenscript-devel@common-lisp.net
>> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>
>
> _______________________________________________
> parenscript-devel mailing list
> parenscript-devel@common-lisp.net
> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>
>
_______________________________________________
parenscript-devel mailing list
parenscript-devel@common-lisp.net
http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel