Imagine that we want to make the following just a little bit nicer:
; Note: this actually works in UPARSE today let result uparse "aaabbb" [ result: gather [ emit x: collect some ["a", keep @(<a>)] emit y: collect some ["b", keep @(<b>)] ] ] else [ fail "Parse failed" ] assert [result.x = [<a> <a> <a>]] assert [result.y = [<b> <b> <b>]]
One Idea: Allow EMIT even if no GATHER is in Effect
Right now there's a little experiment which lets you do this, which assumes you meant to treat the PARSE overall as a gather vs. giving you an error that there's no GATHER:
let result: uparse "aaabbb" [ emit x: collect some ["a", keep @(<a>)] emit y: collect some ["b", keep @(<b>)] ] else [ fail "Parse failed" ] assert [result.x = [<a> <a> <a>]] assert [result.y = [<b> <b> <b>]]
We could do a similar thing for COLLECT. But then we'd have to worry about what to do if you said uparse "..." [emit x: ..., keep ...] -- which was intended as a result?
While this may seem convenient, it isn't very general. It ties in a special relationship between UPARSE and GATHER, which violates some of the modularity.
I'm not thrilled about this, and would rather you made a specialization for it (GATHER-PARSE) that injected the gather above your rule block...so you were explicit about what you were doing.
Another Idea: Bring Back PARSE Return
When RETURN existed in PARSE, we could have said:
let result: uparse "aaabbb" [ return gather [ emit x: collect some ["a", keep @(<a>)] emit y: collect some ["b", keep @(<b>)] ] ] else [ fail "Parse failed" ] assert [result.x = [<a> <a> <a>]] assert [result.y = [<b> <b> <b>]]
Something to think about here would be how RETURN and NULL would mix, in terms of returning null results vs. parse failures. RETURN has kind of an interesting property of rolling on to the next option if its rule fails:
>> uparse "aaa" [ return collect some "b" | return collect some "a" ] == "aaa"
But if you wanted to force a RETURN to give NULL back you could OPT it.
>> uparse "aaa" [ return opt collect some "b" | return collect some "a" ] ; null
But RETURN Was Removed in Ren-C. Why, Again?
I'm a little hazy on the precise complete argument for dropping RETURN...here was the commit from over 2 years ago that did it.
The argument against RETURN may not be as strong as it was, since the idea is that the "main result" of PARSE is trying to be maximally useful to the average callsite...while the progress: is a separate requested output. See The PARSE of /PROGRESS
As a sidenote, Red's PARSE doesn't seem to have RETURN, but it doesn't seem to error either:
>> parse "" [return (10)] == false >> parse  [return integer!] == false
Some of the things that made me uncomfortable about RETURN before are solved. e.g. I didn't like return (code in a group) using the result of the group...and now with the value-bearing concept everything seems to plug together much better. uparse [aaa] [return @(1 + 2)] meets my needs.
I know I didn't particularly care for losing grounding on what exactly it was you were RETURN-ing from...e.g. that it wasn't the function's RETURN. But it's not the same keyword meanings overall. Getting bent out of shape about that one doesn't make much sense.
So...I've Added RETURN To UPARSE
It seems the upsides outweigh the downsides.
But also, in our "flexibility is king" mindset... it seems that RETURN is the kind of thing you should be able to add if you wanted it.
The main thing to do is to keep an eye on what the implications are for the combinator protocol as a whole. Here, we're saying RETURN just bypasses everything and aborts with its result...and being able to abort is necessary for both rule failure and if an exception happens with a FAIL of an ERROR!. So it doesn't complicate the protocol...it just makes the return type for PARSE not necessarily a series type like the input.