Tips for storage in a golfing language
I suggest all of them!
More seriously, they all come in handy some of the time, and the more the better! Implicit input is never bad, just have a flag to turn it off. Variables are helpful so they don't need to be found on a stack or tape; same with registers. Stacks are helpful to store data, and so are tapes. I recommend trying to implement multiple, say a stack and registers, or a stack and variables, like GolfScript. If you can make each function one byte, then your language will probably be effective at golfing, as you can use the advantages of each.
An example:
- Say I want to take two numbers as inputs, and add their string lengths
- Variables might be better for this (a stack might not)
Example code in GolfScript (with implicit input):
~,\,+ ~ # Eval input (e.g. "1 2" > 1, 2) , # Take Length \ # Reverse items on the stack , # Take Length + # Add # Implicit output
However, with variables (I know it's longer, it just doesn't have to swap places on the stack):
~,:a;,:b;ab+ ~ # Eval input , # Get length :a# Store in "a" ; # Discard value left on stack , # Length :b# Store in "b" ; # Discard a # Recall a b # Recall b + # Add # Implicit output
Overloads
Another thing that can be helpful is overloads. For example, if you have a variable storing function, maybe it could be used as a monad (one-input function; I'm not sure of the term out side of J/K/APL) to add to the stack or tape.
Example:
c12 # Stores "1" into register 2
c1] # Pushes "1" onto the stack ("]" is used to denote the end of the monad)
I'd suggest having some quickly usable storage (from the given - tape, queue, stack) and some permanent storage (variables, registers) for things to not get in the way while the program is doing something unrelated. I'd say that much more would rarely give anything and leave more characters free for more 1-byte instructions.
From the definitions given, the ones I think would work the best would be a stack and registers.
A stack, because a tape has to use functions just to store a new thing in it, while a stack should have simple push and pop functions (usually built in the commands).
Registers, because they take less bytes usually compared to variables and if you need to store more than 2-4 different things for something, you're doing something wrong.
Don't limit the functions of them only to what their names or definitions suggest trough - some functions like put the 1st thing of the stack on top of the stack
can definitely help.
There are basically two sorts of storage that need to be handled differently; access to recently generated values and/or the inputs; and long-term storage.
For long-term storage, variables seem to work best; anything with a limited number of options doesn't scale (although languages with this property, like Jelly, nonetheless can do fairly well even on medium-sized tasks). However, there's no need to provide variable names when storing the variable, most of the time; just have a command to store a value in the variable, and autogenerate the names according to a predictable pattern so that storing and retrieving the value can be one command each in simple cases. (For example, you could have commands to restore the most recently assigned variable, the second most recently, the third most recently, and so on, up to a small fixed number, plus a general command that took an argument.)
For short term storage, you want as much to be implicit as possible. Almost all golfing languages I've seen connect the output of one command to an input of the next, by default; the exact way in which this is done differs from language to language but normally comes to the same thing. However, the second input for a 2-input command is more interesting. Taking it from a stack works well in cases where values are only used once, but doesn't scale well when reusing values. (Try to avoid stack manipulation primitives; if you have to resort to using them, you're wasting a lot of bytes.) Alternatively, using user input or a recently assigned variable as the implicit second argument tends to give a few byte savings on simple programs, although you'll then need a parenthesis equivalent to make it possible to place nontrivial expressions on both sides of a binary operator.
In a golfing language I'm working on at the moment, I use a combination of a very cheap mechanism (a single bit) to specify the shape of the parse tree, automatically filling in missing arguments from user inputs by default, and a checkpointing approach that makes it possible to set the default for missing arguments to something else (plus plenty of special cases). At some point I'm likely going to add variables to communicate information greater distances along the program.