Tape and Stack Languages

Concatenative Thinking in Odd Languages

How programs built by joining functions end to end on a stack work, why any split of the text is a valid program, and where esoteric languages echo the idea.

On this page
  1. Composition by writing things next to each other
  2. A useful consequence
  3. No variables, so shuffling words
  4. Quotations: code as a value
  5. Where odd languages echo the idea
  6. A small mental exercise
  7. What the style is good at

Composition by writing things next to each other

In most languages, combining two functions takes some syntax: parentheses, a pipe operator, a variable to hold the intermediate result. In a concatenative language, placing two pieces of code next to each other is the combination. The program A B means do A, then do B, and the two communicate only through a shared stack.

Take a hypothetical word-based notation, similar in spirit to Forth, Joy and Factor:

3 4 + dup *

Reading left to right: push 3, push 4, add them to leave 7, duplicate the top so there are two 7s, multiply them to give 49. Every word is a function from stack to stack. The number words push themselves. The operators take their arguments off the stack and leave a result. The whole line is the composition of five small functions, and the order on the page is the order of execution.

A useful consequence

Because each word takes a stack and returns a stack, you can cut a program anywhere between words and get two valid programs. The first half leaves something on the stack, and the second half continues from it. That makes refactoring unusually mechanical. If dup * appears in many places, you can give it a name such as square and replace each occurrence, with no variables to rename and no parameters to pass.

This is different from the idea behind a typical expression language, where f(g(x)) has to be restructured to extract g(x). In a concatenative language the pieces are already in a line.

No variables, so shuffling words

With no named arguments, values are kept in place by stack manipulation words. Typical ones are duplicate, drop, swap and rotate. A program that needs the same value twice duplicates it; one that needs two values in the opposite order swaps them. Programmers often find that good code in this style is short and that a word needing more than three or four items from the stack is a sign it should be split up.

This is also where the style gets its reputation for being hard to read. Without names, the only guide to what the stack holds is a comment or your own memory, which echoes the code readability problem shared by many odd languages. One practical habit is to annotate each definition with a short note of what it takes and leaves.

Quotations: code as a value

Concatenative languages commonly let you wrap a piece of code in brackets without running it. The wrapped code, often called a quotation, sits on the stack like any other value. Another word then runs it, or runs it several times, or chooses between two of them.

[dup *] call

Here the bracketed part is pushed unevaluated, and call takes it off and runs it on whatever lies underneath. Conditionals and loops are built this way. An if is a word that takes a flag and two quotations and runs one of them. There is no need for special syntax for control flow, because control flow is just words that consume quotations.

This idea reappears in tiny stack languages that manipulate strings of code, where parenthesized text is stored, copied, joined and finally executed. It is the same trick in a plainer form.

Where odd languages echo the idea

Not every stack language is concatenative. Befunge puts numbers on a stack and does follow the postfix style for arithmetic, but its control flow depends on a two-dimensional grid, so you cannot cut the text at any point and expect two programs. In the Befunge language, 23+4*. still means (2 + 3) * 4 and prints 20, and the program stack behaves just as a concatenative runtime would. The difference is that the surrounding code can send the pointer in any direction.

Pure concatenative languages are also close relatives of other formal systems. The lambda calculus builds everything from function application, whereas the concatenative view builds everything from composition. Combinators such as those used in combinator calculus sit between the two, since they too are small fixed functions glued together. Seeing the connections helps explain why these languages attract people who like to think about what a program really is.

A small mental exercise

Try to predict the stack after each word of 2 3 swap - 5 *. Start with 2, then 3, giving 2 below 3. Swap puts 3 below 2. Subtract takes the top as the second operand, giving 3 minus 2, which is 1. Then 5 is pushed and multiplied, leaving 5. Working through a few lines like this is the quickest way to get comfortable with the style, and it prepares you for the rules of how a language defines syntax and semantics separately.

What the style is good at

It is compact, composes naturally and has simple implementations: a basic interpreter is little more than a loop over words and a stack. It is less comfortable for programs with many long-lived values. Neither is a flaw, and the experiment of writing something real in such a language shows which kind of problems fit.

The complete guide to tape and stack languages

Tape and Stack Based Esoteric Languages
Read the guide

Keep exploring

Guides from other topics

All topics →

What Is Befunge?

A plain introduction to Befunge, the esoteric language where code lives on a two-dimensional grid and an instruction pointer moves in four directions.

Befunge Basics 5 min read