Menu

Using R@

thebeez

I first encountered this technique when I was studying the FIG-Editor. The author explained how he used TORS (Top OF Return Stack) as a kind of "read-only" local variable. You can put an address, limit or other constant there and retrieve it with a single R@.

In the years to follow I've used this technique a lot of times. One of the best pieces of Forth advise I ever got. When [The 4tH preprocessor] came to be, it only had four "registers". I added the string stack much later on. Still, having four variables at your fingertips was quite handy.

So, just before the release of v3.62.5 I got an idea. Yes, we had R@. In the meanwhile I had added R'@ as an "inline macro" - not as a primitive. And sometimes I was in trouble and "abused" J to retrieve the third item from the return stack. Everything was in place - except for the vision.

I decided to turn R'@ into a primitive and introduce R"@. I'm not too proud of that name, but it seemed logical to keep up with the convention. Now we had three words that could retrieve TORS, 2ORS and 3ORS. I had high expectations for these, but at c.l.f. there were people who claimed to have "been there and done that". To no avail, of course.

But I'm Dutch and hence very stubborn. So I implemented it anyway. While writing code for v3.63.0 I used this new feature. I also learned you could create an alias for R@, R'@ and R"@ using AKA. Which IMHO makes the code a lot easier to read. This is from a "Midpoint Circle" algorithm:

aka r@  x0
aka r'@ y0

: circle                               ( x y radius --)
  swap >r swap >r 1 over - swap 0      ( dp x y R: x0 y0)

  begin
    over x0 + over y0 +                set_pixel ( x0 + x, y0 + y)
    over x0 swap - over y0 +           set_pixel ( x0 - x, y0 + y)
    over x0 + over y0 swap -           set_pixel ( x0 + x, y0 - y)
    over x0 swap - over y0 swap -      set_pixel ( x0 - x, y0 - y)
    over y0 + over x0 +           swap set_pixel ( x0 + y, y0 + x)
    over y0 + over x0 swap -      swap set_pixel ( x0 - y, y0 + x)
    over y0 swap - over x0 +      swap set_pixel ( x0 + y, y0 - x)
    over y0 swap - over x0 swap - swap set_pixel ( x0 - y, y0 - x)
                                       ( dp x y R: x0 y0)
    1+ >r over 0> if 1- r@ over - else r@ then 2* 1+ rot + swap r>
    over over <                        ( dp x y f R: x0 y0)
  until drop drop drop r> drop r> drop
;

Note it is still possible to use the return stack in a more traditional way - even if you've AKA'd any of the "return stack fetch" words. You simply use R@ to distinguish between them. Of course, you still have to take care - it is still Forth. Note that it doesn't need to clobber your symboltable - you simply HIDE the aliases when you're done.

Personally, I think it adds something to the language. Especially when you're juggling with a lot of parameters - yes, you can do clever things in Forth, but some algorithms simply come with a lot of parameters.

On the other hand, I also understand people who criticize it with the argument "a stack is not an array". I completely subscribe to that view - that's why I never implemented PICK and ROLL in the core language.

But there is IMHO a difference between turning the entire stack into an array or just three elements of it. In essence, this facility has always been there. If you wanted to use it before these changes, you could. If you don't want to use it now, then don't.

However, if you want to use it, it just became a lot easier, more consistent, more compact and much faster.


Related

Wiki: The 4tH preprocessor