In the wandering history that preceded PACK! antiforms and modern multi-returns, there was for a time a feature called "RETURN PROXYING".
What happened was that some of those old designs had separate frame cells for all their individual multi-return values. I got the idea to think of slipping in variables to those cells as inputs... telling the function machinery where to write.
So when you'd see something like [x]: negate 10 you might think of it as slipping a bound X word into the RETURN: slot of NEGATE as input.
On the surface that seems like it might even be useful more generally:
>> f: make frame! negate/
>> negate.return: $x
>> negate.value: 10
>> eval f ; the hidden X is written back automatically
== -10
>> x
== -10
It's A Broken Idea for a LOT of Reasons
I'm not going to rehash the history of Multi-Returns for you. You can read all about it if you're interested:
https://rebol.metaeducation.com/t/the-history-of-multi-return-in-ren-c/1134
But I'll just point out that when the left hand side of a SET-BLOCK! had to inject variables rightward to be written to, you face problems from something as simple as parentheses:
[x]: (((negate 10)))
How do SET-BLOCK! and NEGATE get connected up together to tunnel the X WORD! through? And what happens if a function is using the variable you passed it to write to, but it has an error before it completes and it leaves the variable in an incomplete state?
This isn't even mentioning the fact that not every function generator made functions with a RETURN slot. LAMBDA didn't have one (and still doesn't, even though it has type checking now and answers the RETURN OF question...)
PACK! antiforms are the far superior answer.
But Just Because It Sucks Doesn't Mean We Can't Do It!
It's certainly something you could choose to do if you were writing your own function generator.
In fact, it's trivial to write a wrapper for it! Just add a :RETURN to the public interface, and write back to it if it's supplied:
returnproxy: lambda [action [action!]] [
enclose (augment action/ [:return [word!]]) f -> [
(opt f.return): eval f
]
]
That means you can pass it as a refinement:
>> test: lambda [x] [x + 1000]
>> wrapper: returnproxy test/
>> y: ~
>> wrapper:return 20 $y
== 1020
>> y
== 1020
Or you can use it with a frame:
>> out: ~
>> f: make frame! wrapper/
>> f.x: 20
>> f.return: $out
>> eval f
== 1020
>> out
== 1020
That's pretty cool for something pretty lame! I'll just put it here again to show how simple it is to do such things
returnproxy: lambda [action [action!]] [
enclose (augment action/ [:return [word!]]) f -> [
(opt f.return): eval f
]
]
Note that due to the way it's opting out of the assignment, it works smoothly if you don't supply the :RETURN refinement... it's null, becomes void, and just ignores the effective ():
>> wrapper 304
== 1304
![]()