In trying to make a shim where MAP-EACH splices and has /ONLY, I thought the easiest way of doing it might be to redo it in terms of a COLLECT of a FOR-EACH.
This delves some into the ambition of Ren-C to raise the bar for Rebol, so it raises some questions. First, let's try a naive approach:
map-each: function [
{https://forum.rebol.info/t/1155}
return: [block!]
'vars [blank! word! block!]
data [any-series! any-path! action!]
body [block!]
/only
][
collect [
for-each :vars :data [
keep/(only) do body
]
]
]
One reason this won't work correctly is because BODY is executed via a "link" of DO instead of being embedded into the body of the FOR-EACH. That means it won't bind to the VARS variables. And in a definitional-break-and-continue world (which I've been considering) it won't have words like CONTINUE or BREAK bound.
We can address that by embedding the code in, let's say we just put it in as a GROUP!
collect [
for-each :vars :data compose [
keep/(only) (as group! body) ; only inside path, not a compose/deep
]
]
This simple implementation supplements the body of the MAP-EACH with additional code. It does it by composing in the body code as a GROUP!, so that it will pick up any bindings the FOR-EACH would add.
Fair enough, but it has a couple of problems. One problem: what if I said map-each keep [1 2 3] [...]? :-/ Our supplemental body adds code that the user doesn't see at the callsite, so they don't know to avoid usage of words that are in that supplemental body for their loop variables. This gets worse the more supplemental code you have.
I think we need to expose something lower-level than FOR-EACH
Really it seems like what you need here is a tool that lets you set up whatever binding object a loop is going to use, gives you a chance to bind code to that object, then lets you run the iteration independent of binding. Something like:
collect [
[context looper]: make-loop-stuff :vars :data
bind body context
while [looper] [
keep/(only) do body
]
]
The imagined MAKE-LOOP-STUFF would give you two things back: a context to bind any code into that you wanted to see the changes to variables in, and a function you could call that would update the values of those variables as long as there was more data.
....Just another epicycle of the binding problems...
Binding in Rebol will always be Rube-Goldberg-like, and so the question is how to maximize the fun and minimize the annoyance, while still getting decent performance. I think if people can think of FOR-EACH as a higher level "macro" which makes a lot of assumptions in order to be ergonomic, they can realize that writing their own loop is going to involve digging deeper.
Something like the pattern above could be used to implement FOR-EACH, MAP-EACH, or REMOVE-EACH...though they could retain their native optimized versions. There's still worries about mutating the bindings on passed-in code (the bind body context above) so a "good" answer would be something like body: in context body where that was understood to not modify the original, but give a rebound "view" at a lower cost.