Funge-98

Funge-98 Fingerprints and Extensions

How Funge-98 fingerprints load extra command sets into the letters A to Z, why that makes the language extensible, and what to watch for in portable programs.

On this page
  1. The problem fingerprints solve
  2. What a fingerprint is
  3. Loading and unloading
  4. Why this makes the language extensible
  5. The costs of extensibility
  6. A mental model

The problem fingerprints solve

Every language that lasts eventually needs features its designers did not plan for. In a grid language the problem is sharper than usual, because each instruction is a single character and there are only so many printable characters to go around. Funge-98 handles this with a mechanism called fingerprints. Instead of assigning every useful operation its own permanent symbol, the language reserves the twenty-six capital letters A to Z for instructions that a program loads when it needs them.

By default, those letters do nothing useful: an interpreter treats them as unknown instructions, which reflect the pointer. After a program loads an extension, some or all of the letters take on new meanings defined by that extension. A different extension can assign the same letters different meanings, and a program can unload it again later.

What a fingerprint is

A fingerprint is a number that identifies an extension. By convention it is built from a short name, typically up to four capital letters, with each character treated as one byte and the bytes combined into a single integer. So a name like ROMA is really a number whose digits are the character codes of R, O, M and A. The names are mnemonic only; the interpreter looks the number up in its table of supported extensions.

Since the identity is a number, a program can compute it, push it on the stack, and ask for it at run time. This is what makes the system work inside the language itself, without separate declaration syntax.

Loading and unloading

Two instructions do the work: an opening parenthesis loads, and a closing parenthesis unloads. To load, the program pushes the characters of the name, then pushes a count of how many there are, then executes the load instruction. The interpreter pops the count and the values, forms the fingerprint, and either maps the extension’s instructions onto letters or reflects the pointer if it does not know that name.

The order of pushing matters because the stack is last in, first out. For a four-letter name, the most significant letter is popped first, so you push the letters in reverse. Here is a small example, assuming your interpreter supports the Roman numeral extension:

"AMOR"4(XV+.@

String mode pushes A, M, O and R, so R ends up on top. The 4 is the count. The opening parenthesis loads the extension named ROMA. After loading, the letters X and V, which that extension defines as the numeric values of the Roman numerals, push 10 and 5. The plus adds them, the dot prints 15, and the at sign ends the program. Depending on the interpreter, loading may leave extra values on the stack, which is harmless here because the program only uses the top of the stack afterward. If the extension is unavailable, the pointer reflects and the program will not do what you expect, so a real program should be prepared for failure.

Why this makes the language extensible

The core idea is separation. The specification defines a stable core of instructions and a single, small mechanism for adding more. Anyone writing an interpreter can offer extensions the original authors never imagined, and programs that need them ask for them by name. Common categories of extension include extra math, string handling, time and date access, and ways to interact with the host system, among many others. Which ones exist is a matter of the interpreters and the community registry rather than the core language, so treat any list you read as incomplete.

Another benefit is that extensions are layered. Loading a second extension that uses some of the same letters generally stacks its meanings on top of the first, and unloading pops that layer, restoring the earlier behavior. This means a library-like routine can load what it needs and clean up afterward, leaving the caller’s view of the letters unchanged.

The costs of extensibility

Extensions are a tradeoff, and it is worth being clear about that. A program that depends on a fingerprint is only as portable as the interpreters that implement it. A program written for one interpreter’s extension may fail silently, or reflect, on another. This is one reason careful programmers load extensions defensively and keep the optional parts of a program separate from the essential parts. The article on extension loading explores that discipline more fully.

Extensions also raise a documentation challenge. Because the letters change meaning depending on what has been loaded, reading a grid is harder: an M in one region might mean something quite different from an M in another. A comment, or a layout convention that groups extension-dependent code together, helps. It is the same reason people care about esolang documentation in general.

Finally, extensions can reach outside the language, for example to the file system or to the host machine. Whether and how to allow that is a decision for the interpreter, and a sensible one will let the user restrict it.

A mental model

Think of the letters A to Z as empty sockets, and a fingerprint as a plug that fills some of them. The language gives you the sockets and the plugging mechanism; the community and the interpreters supply the plugs. That division is much of why the design has stayed interesting. It is also a miniature of a problem found throughout language design: how to leave room to grow without letting the core become unwieldy. The broader set of Funge-98 features builds on the same careful balance, and the specification itself is a good model for anyone who wants to write a language specification of their own.

The complete guide to funge-98

What Is Funge-98?
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