Comments on: Value return after computation
There is a simple and general pattern in code that takes this form:
a: do [b: c d b]
We want the value of c (the first expression) returned, not d (the second) so we must use b as a temporary in order to return it.
For example:
rev: func [str /local tmp] [
tmp: copy str
reverse str
tmp
]
This code seems longer than we need. We'd prefer to write:
a: c d
but, of course, that returns d and not c.
Stated in English it is "return the result of the first computation". We want to follow the return value with a function that is not to be computed but not returned.
We could invent a function that evaluates c and d but returns c:
a: follow [c d]
The follow function could be defined as:
follow: func [blk] [first reduce blk]
(Another possible name might be after. But, let's just use follow for right now and think about the name later.)
Now, the above example becomes:
rev: func [str] [
follow [
copy str
reverse str
]
]
Of course, there is additional overhead in that approach: an extra function call and a block reduction. An alternative is:
rev: func [str] [
follow copy str reverse str
]
This form can be defined as:
follow: func [a b] [:a]
There has also been a suggestion to add an /after refinement to return. For example:
rev: func [str] [
return/after copy str reverse str
]
This is trivial to implement, but it is only effective for function return cases, not the general pattern. But, maybe the function return case is the most common.
If you have ideas or insights, I welcome you to post a comment.
15 Comments Comments:
John Niclasen 25-Jun-2007 14:47:14 |
It's a bit unclear to me, why anyone would have this functionality. If I understand it correctly, result is, that the original variable is changed, and we got a new copy to continue to work with.
Why put this functionality in a function? It's more clear, if I make the copy myself before calling a function. Then the function just do, what it should (reverse a string for example) with no side-effect, and I can continue work with my copy.
Maybe I need some more examples. | Brian Tiffin 25-Jun-2007 15:15:46 |
I like the feature, but why bury the first. Make it position (or rank or...) and allow the coder to pick the result expression. Along the lines of
do [rank [expr1 expr2 expr3 expr4] 2]
Icon has this with the selected coevaluation.
Then again, maybe that makes for slower groks of codespace.
Once again, thanks for listening
Cheers | Brian Hawley 25-Jun-2007 21:15:59 |
John, in the example REV function given above, the string assigned to the tmp word in the function remains assigned to that word even after the function returns. It doesn't get reassigned until the function is called again. While the string is assigned to that word it will not get collected by the garbage collector, a memory leak.
If you do the temporary as a side effect of the application of func [a b] [:a], the temporary is assigned to the word in that function. If that function is used pervasively it will get called again much sooner, so the memory leak won't last as long, and can be undone easily. | John Niclasen 26-Jun-2007 2:18:05 |
The reason to copy str in the start of REV and return it must be, that the original version of str is needed for something.
So I can write:
tmp: copy str
reverse str
and then continue work with tmp. Sure, I then have this extra word tmp, but the REV example returns tmp, and it must go somewhere. In what situations would I need the functionality like with the REV function? | Chris 26-Jun-2007 6:32:35 |
John, here is an example:
pop: func [series /local result][
result: last series
remove back tail series
result
]
Refined with e.g. return/after
pop: func [series][
return/after last series
remove back tail series
] | -pekr- 26-Jun-2007 9:35:21 |
Chris:
pop: func [series][
before remove back tail series last series
]
Carl:
rev: func [str][
before reverse str copy str
] | John Niclasen 26-Jun-2007 10:40 |
I'm beginning to see the point! ;-)
A native POP function doing this:
pop: func [stack /local r] [
r: first stack
remove stack
r
]
might come in handy. Then a PUSH should work just like INSERT. It's possible to make a BEFORE today (with blocks though):
before: func [blk1 blk2 /local r] [
r: do blk2
do blk1
r
]
before [remove stack] [first stack]
It's a bit backward REBOL thinking to me. Remember: "Everything should be made as simple as possible, but not simpler."
On which side of the barrier is this functionality? | Carl Read 27-Jun-2007 4:23:35 |
The functionality I'd like would work like this:
Without it...
>> do [1 2 print "a" print "b" 3]
a
b
== 3
With it...
>> do [1 foo 2 print "a" print "b" 3]
a
b
== 2
In other words, whatever is returned by the expression after foo is what's returned at the end of the block's evaluation, as apposed to what's returned by the last expression evaluated. (RETURN in functions would override this, but maybe not EXIT.)
I'll leave it for others to decide how easy or otherwise it'd be to add to Rebol - and what's a suitable word for foo.
| Carl Sassenrath 28-Jun-2007 12:31:18 |
Just a note on:
"A native POP function doing this..."
It exists and it's called TAKE (because it's not really a POP, and we may want to use POP for other things.)
See: TAKE function | Brian Hawley 28-Jun-2007 13:27:15 |
I like the name AFTER for after: func [a b] [:a] because it makes expressions like return after ... make more sense. It reminds me of the get in ... code pattern for getting the values of words that are in an object. | Gregg Irwin 29-Jun-2007 5:27:31 |
If 'first-of wasn't confusing, because of things like 'maximum-of, I think I would like that. The hard part about having a concise name for this is that it needs to make clear, IMO, what it does; that it takes two args are returns the result of the first. 'First-of-two could work I suppose.
I don't see it as something a lot of people will use a lot of places, so I'm OK with a longer name. For those who do use it a lot, they can always define a shortcut. | Anton Rolls 29-Jun-2007 22:34:38 |
I like Gabriele's point of view, but my preferred name is still CARRY-OVER. Such a longer name is deserved for such a specific function, eg:
carry-over a b
carry-over a (b c)
I don't like BEFORE, which reverses the order of evaluation. Can you imagine the confusion when one of the blocks is very large ?
So my secondary vote goes to AFTER. | rebolek 2-Jul-2007 12:27:55 |
I like AFTER. It's short and descriptive enough. carry-over seems too long to me. | maxim olivier-adlhoch 15-Aug-2007 22:46:12 |
an example for the use of after:
return after result result: none
which clears the gc for the 'result word
right now we have to do:
return first reduce [reduce reduce: none]
maybe a better name for 'after could be 'keep ?
ex:
return keep result result: none
| Mark Ingram 24-Aug-2007 13:47:16 |
I think Maxim meant:
return first reduce [result result: none]
Rather than this, though, how about a /wipe refinement
to 'func (or possibly as a positional in the spec block)
that sets all (succeeding) arg words to none! after
the function returns? I can't say if this would obviate
the need for AFTER (which is the name that gets my vote),
but if it is gc behaviour we are fixing, let's fix it.
|
Post a Comment:
You can post a comment here. Keep it on-topic.
|