A Few Thoughts on OO RPN

RPN goes back a long way, and it is very useful for a large number of applications in computer science. But it has something of a problem when dealing with object-oriented programming.

A number of efforts have been tried over the years - there were several OO Forths for the Mac (NEON and YERKS as I recall) but they never became mainstream. The principle problem is a conceptual one: RPN works only with the top of the stack. This is fine so long as the top of the stack has the object you want to work with, but that isn't always the case. Take, for example, a simple procedure to construct an array:

 1 3 4 2 6 5 ArrayX sto

Notice that we need to accumulate the objects to be placed in the array on the stack before the actual array object that will work with them is pushed, and because there is no natural line of demarcation between the parameters to ArrayX and other parameters already on the stack, we must therefore push a count of the number of args we wish to add to ArrayX before pushing ArrayX itself. Only after all this fol-de-rol can we send the actual message to store and array.

Some time ago I was toying with these ideas and came up with a solution. Imagine, if you will, that there are actually two stacks - call them param and exec. In the normal course of the computation, the param stack is used as it always has been:

 1 2 3 + - print

giving us (2+3)-1. But suppose we added an operation to transfer the top of the param stack to the top of the exec stack and another for vice versa. Think of the two stacks as opposed and we will use the words DOWN and UP, meaning to drop the top of the param stack DOWN to the top of the exec stack, and UP doing the reverse. Now, all we need to have is some way of distinguishing messages from other parameters. Let's use a prefix ":". Finally, each object pushed onto the exec stack is able to remember where it used to be in the param stack. So, building an array:

 ArrayX DOWN 1 3 4 2 6 :sto UP

ArrayX is pushed on to the param stack, then popped to the eval stack. It knows where it used to be in the param stack, and so it knows that anything above that point must be params for it to use when a message is sent to it. We have a natural bracket around the params, so we don't need to count the parameters. Notice that these operations stack very nicely. Suppose we haven't created ArrayX yet and must do so before initializing it:

 Array DOWN 'ArrayX' :create DOWN 1 3 4 2 6 :sto UP UP DROP

Now, the DOWN and UP ops look really ugly, but there is an alternative to using them. Since rpn does not otherwise need parens, we can use '(' for DOWN and ')' for UP:

 Array ( 'ArrayX' :create ( 1 3 4 2 6 :sto ) )

The parens neatly enclose the objects that will be parameters to the executing object, and the appropriate messages now can be directed to specific objects.

In actual practise, I used a simpler method. When '(' was encountered I simply pushed the current top of stack pointer, when ')' came along I popped it. The above leaves Array and ArrayX on the top of the stack. If we wanted to be neater we would need something like:

 Array ( 'ArrayX' :create ) SWAP DROP ( 1 3 4 2 6 :sto )

This leaves ArrayX on the top of the stack. You can also chain various ops:

 Array ( 'ArrayX' :create ) SWAP DROP ( 1 3 4 2 6 :sto ) :print :save ArrayX ( 3 :index ) :print

This would print out ArrayX, save it in memory, recall it to the stack and extract the 3rd element (a 4) and then print that.