Encodings and Enumerators

Suppose you want to feed a Turing machine into another Turing machine, as data. Machines are abstract mathematical objects with states and transition tables. But the tape of a Turing machine only holds symbols from a finite alphabet. To reason about machines that process machines — compilers, verifiers, interpreters, self-referential arguments — we must first fix a way to write any such object down. It must become a finite string over a fixed alphabet. This is the problem of encoding. It is the quiet piece of bookkeeping that makes every deep result in computability theory expressible at all. Once objects are strings, a second question arises. What does it mean to generate all strings in a language? This differs from merely testing whether a given string belongs to it. This is the role of the enumerator, a device that prints a (possibly infinite) list of strings. It never stops if the language is infinite. Enumerators formalize the intuitive notion of "listable" or "recursively enumerable" sets. They connect the algorithmic notion of recognition to the combinatorial notion of listing. These two ideas — encoding and enumeration — are not mere technicalities. Real systems depend on them constantly. Source code is an encoding of a program that a compiler (itself a machine) reads as a string. A search engine's crawler enumerates the web without ever "deciding" it is complete. A proof assistant enumerates theorems derivable from axioms. The encoding convention ⟨M⟩\langle M \rangle for a machine MM is what lets us build a <span class="wiki-link-missing">Universal Turing Machine</span>. This is a single machine that simulates any other machine given as input. It is the technical foundation beneath the Church-Turing Thesis's claim that "algorithm" has one true mathematical meaning. This article makes precise how to encode arbitrary discrete objects (graphs, automata, machines) as strings. It defines enumerators rigorously, and proves the central equivalence theorem: a language is Turing-recognizable if and only if some enumerator lists it. We build the encoding-decoding machinery step by step. We give a full pseudocode construction converting a recognizer into an enumerator and back. We also trace a worked example over a small language.

Key takeaways
  • Encoding fixes a way to represent any finite discrete object (graph, DFA, TM) as a string over a fixed alphabet, written ⟨O⟩\langle O \rangle.
  • Encodings must be effective (computably constructible) and decodable, so machines can manipulate objects as data.
  • An enumerator is a Turing machine variant with a printer that outputs strings of a language, one after another, possibly forever.
  • Theorem: a language is Turing-recognizable iff some enumerator enumerates it — recognizers and enumerators have equal power.
  • The "recognize ⇒ enumerate" direction needs dovetailing (interleaved simulation) so no single input can starve the process.
  • Encodings and enumerators together underlie the Universal Turing Machine and every diagonalization argument in Decidable and Turing-Recognizable Languages.
The core insight

Once every object is a string via a fixed encoding, "recognizable" and "enumerable" become the same computational power viewed from two directions: testing membership versus generating a list.

The idea

An encoding of a discrete mathematical object OO (a graph, a Turing machine, a polynomial, a proof) is a string ⟨O⟩\langle O \rangle over some fixed finite alphabet Σ\Sigma, produced by an effective, injective procedure: distinct objects get distinct strings, and there is an algorithm that reconstructs OO from ⟨O⟩\langle O \rangle. The specific bit-level details of an encoding scheme are never important — what matters is that some reasonable encoding exists, because any two reasonable encodings are interconvertible by a Turing machine in a way that changes no decidability or recognizability question. This is why textbooks freely write things like ⟨G,k⟩\langle G, k \rangle for a graph paired with an integer, trusting the reader to accept that graphs, integers, and tuples can all be serialized into one string. When we say a Turing machine MM takes "another machine NN" as input, we mean formally that MM receives the string ⟨N⟩\langle N \rangle and must decode the transition table, state set, and start/accept/reject states from it before simulating NN. An enumerator is a variant of the Turing machine model equipped with an extra output device — a printer — attached to a second, write-only tape. An enumerator EE has no explicit input; it runs forever (or halts) starting from a blank work tape, and periodically it copies the current contents of a work tape onto the printer tape followed by a special separator symbol, effectively "printing" a string. The language enumerated by EE, written L(E)L(E), is the set of all strings that appear on the printer output at some point during the (possibly infinite) run. If EE never halts but keeps printing forever, it may still enumerate a language completely, as long as every string of L(E)L(E) eventually appears — it is allowed (indeed expected, for infinite languages) that no finite prefix of the run has printed everything. Two enumerators can enumerate the same language while printing strings in wildly different orders, with or without repetition; none of this affects which language is enumerated, only the algorithm's internal behavior. This freedom is what lets an enumerator built from a recognizer use a slow, repetitive strategy (dovetailing) and still correctly enumerate the target language, which is the crux of the main theorem below.

How it works

The equivalence between recognizers and enumerators is proved by two explicit constructions, each turning one kind of machine into the other.

Direction 1 — Enumerator ⇒ Recognizer. Given an enumerator EE for language LL, build a recognizer RR: on input ww, simulate EE, and each time EE prints a string, compare it to ww; if it matches, RR accepts. If EE runs forever without printing ww (because w∉Lw \notin L), RR simply never halts — which is fine, because recognizers are only required to halt and accept on strings in LL; they may loop on strings not in LL.

Direction 2 — Recognizer ⇒ Enumerator (the subtle direction). Given a recognizer MM for LL, we want to build an enumerator EE that prints exactly the strings MM accepts. The naive idea — go through w1,w2,w3,…w_1, w_2, w_3, \dots in the standard order of all strings over Σ∗\Sigma^*, run MM on w1w_1 to completion, then on w2w_2, and so on, printing wiw_i whenever MM accepts it — fails, because MM might not halt on w1w_1 (recall MM is only guaranteed to halt on strings in LL). The construction would hang forever on the first non-halting input and never reach w2,w3,…w_2, w_3, \dots, even if they are in LL. The fix is dovetailing: instead of running each simulation to completion before starting the next, run all of them simultaneously, each for a bounded number of steps, in a carefully staggered order that guarantees every pair (wi,step budget j)(w_i, \text{step budget } j) is eventually tried. ``` ENUMERATOR-FROM-RECOGNIZER(M): // M is a Turing machine (recognizer) for language L. // Produces an enumerator E that prints exactly the strings in L, // each at least once, in some order (possibly with repeats). let w_1, w_2, w_3, ... be all strings in Sigma* in standard order (length order, then lexicographic within each length) for i = 1, 2, 3, ... : // outer counter: "round" i for each pair (p, q) with p + q = i, p >= 1, q >= 1: // p indexes which string, q is the step budget simulate M on input w_p for exactly q steps if M accepts w_p within those q steps: print w_p // if M rejects or has not halted within q steps: do nothing, move on // E never halts (unless L is finite and we choose to stop early, // which is optional and not required) ``` Every string wp∈Lw_p \in L is accepted by MM in some finite number of steps q0q_0. The pair (p,q0)(p, q_0) appears at round i=p+q0i = p + q_0 in the double loop, so EE is guaranteed to print wpw_p at that round (and possibly again at later rounds, which is harmless). Strings not in LL are never printed, because MM never accepts them, regardless of how many steps we simulate. Hence L(E)=LL(E) = L exactly. The diagram below shows the dovetailing schedule as a grid: rows are candidate strings wpw_p, columns are step budgets qq, and the diagonal sweep order is how EE actually visits cells.

Figure
Dovetailing: diagonal sweep over (string, step-budget) pairs
Simulate M on w_p for q steps — visit order follows diagonals p+q=i w_1 w_2 w_3 w_4 w_5 q=1 q=2 q=3 q=4 q=5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 6 7 8 9 10 Cell (p,q) is visited at round i = p+q. If M accepts w_p in ≤ q steps, w_p is printed at round i. Every accepting (p, q0) pair sits on some finite diagonal — so it is reached.

The core mechanism generalizes far beyond this pair: dovetailing is the standard technique whenever infinitely many potentially-nonhalting processes must all be given a "fair share" of computation. It reappears in constructing the Universal Turing Machine simulation strategy for time-bounded diagonal arguments and in enumerating all provable statements of a formal system.

Complexity & correctness

Correctness of Direction 1 (enumerator ⇒ recognizer). RR accepts ww exactly when ww eventually appears in EE's output, i.e., exactly when w∈L(E)=Lw \in L(E) = L. If w∉Lw \notin L, EE never prints ww, so RR never finds a match and runs forever — consistent with RR being a recognizer (not necessarily a decider) for LL. No false accepts are possible since RR only accepts on an exact string match against EE's genuine output.

Correctness of Direction 2 (recognizer ⇒ enumerator). Let L=L(M)L = L(M).

  • Soundness (L(E)⊆LL(E) \subseteq L): EE only prints wpw_p when its simulation of MM on wpw_p reaches an

accepting configuration within the allotted qq steps — a genuine, faithfully simulated accept of MM. Hence every printed string is truly in LL.

  • Completeness (L⊆L(E)L \subseteq L(E)): Fix any wp∈Lw_p \in L. Since MM recognizes LL, MM halts and accepts

wpw_p after some finite number of steps q0q_0. The double loop enumerates every pair (p,q)(p,q) with p,q≥1p, q \geq 1 exactly once, organized by increasing sum i=p+qi = p+q; since there are only finitely many pairs with a given sum, every specific pair — including (p,q0)(p, q_0) — is reached after finitely many rounds. At that point EE simulates MM on wpw_p for at least q0q_0 steps and detects the accept, so wpw_p is printed. Together these give L(E)=LL(E) = L, establishing the Enumerator-Recognizer Equivalence Theorem: a language is Turing-recognizable if and only if it is enumerable by some enumerator. This is exactly the theorem that justifies the alternate name "recursively enumerable" for Turing-recognizable languages, tying directly into the classification of languages developed alongside Decidable and Turing-Recognizable Languages.

Complexity, honestly. These constructions are about computability, not efficiency — there is no polynomial-time bound to state here, and none is claimed. The dovetailed enumerator can be catastrophically slow: to print a single short string wp∈Lw_p \in L discovered only after MM needs q0=106q_0 = 10^6 steps, EE must first complete rounds 11 through p+q0−1p + q_0 - 1, wastefully re-simulating many other candidates from scratch (a naive implementation restarts each simulation at step 1 every round, giving a step count that grows roughly quadratically in the round index for a fixed candidate, i.e., overall super-linear blowup relative to MM's own running time). This is an accepted cost: the theorem is purely about which languages are enumerable, not about doing so quickly. A refined version keeps partial configurations across rounds instead of restarting, saving redundant work, but the asymptotic classification (recognizable ⟺ enumerable) is unaffected either way, since we only need some enumerator to exist.

Encoding correctness. For an encoding scheme ⟨⋅⟩\langle \cdot \rangle to be sound for these arguments, it must be: 1. Total and effective — an algorithm computes ⟨O⟩\langle O \rangle from any valid OO. 2. Injective (up to a fixed canonical form) — distinct objects map to distinct strings, so decoding is unambiguous. 3. Decodable — an algorithm recovers OO (or detects "invalid string") from ⟨O⟩\langle O \rangle. Any two encoding schemes satisfying these properties are interchangeable via a computable translation, so no computability result (recognizability, decidability, or the enumerator equivalence above) depends on which reasonable encoding is chosen — only on the fact that some reasonable encoding exists.

Worked example

Let L={anbn:n≥0}L = \{a^n b^n : n \geq 0\}, over Σ={a,b}\Sigma = \{a,b\}, a classic non-regular but decidable (hence certainly recognizable) language. Take MM to be a straightforward Turing machine deciding LL by repeatedly crossing off one leading aa and one trailing bb, running in O(n2)O(n^2) time on inputs of length 2n2n (each pass is O(n)O(n), and O(n)O(n) passes are needed) — but for this example the exact runtime doesn't matter, only that MM halts on every input (accepting strings of the form anbna^nb^n, rejecting all else). Standard order over {a,b}∗\{a,b\}^* begins:

ε, a, b, aa, ab, ba, bb, aaa, aab, aba, abb, baa, bab, bba, bbb, …\varepsilon,\ a,\ b,\ aa,\ ab,\ ba,\ bb,\ aaa,\ aab,\ aba,\ abb,\ baa,\ bab,\ bba,\ bbb,\ \dots

so w1=ε,w2=a,w3=b,w4=aa,w5=ab,…w_1=\varepsilon, w_2=a, w_3=b, w_4=aa, w_5=ab, \dots Only ε\varepsilon (n=0n=0), abab (n=1n=1), aabbaabb (n=2n=2), etc. belong to LL. Because MM is a decider here (it always halts), the trace is simpler than the worst case, but it still shows the dovetailing schedule concretely. The table shows which round i=p+qi=p+q first causes EE to discover and print each in-language string, assuming (for illustration) that MM needs q0(wp)q_0(w_p) steps as listed.

ppwpw_pin LL?q0q_0 (steps MM needs)round i=p+q0i=p+q_0 printed
1ε\varepsilonyes12
2aano—never
3bbno—never
4aaaano—never
5ababyes49
6babano—never
7bbbbno—never
...............
11aabbaabbyes920

Even though ε\varepsilon (round 2) and abab (round 9) are far apart in output order, EE still reaches each of them eventually because every finite (p,q0)(p, q_0) pair sits on some finite diagonal. The output stream of EE is therefore ε,ab,aabb,…\varepsilon, ab, aabb, \dots interleaved with the many wasted rounds spent on strings never destined to be accepted — those rounds simply produce no printed output and are silently skipped. This matches the general argument: soundness (only true members are printed) and completeness (every member is eventually printed) both hold, exactly as claimed above.

Common pitfalls & traps

PitfallWhy it's wrongFix
"Run MM to completion on w1w_1, then w2w_2, …" without dovetailingHangs forever if MM doesn't halt on some early wi∉Lw_i \notin L; later strings in LL are never reachedInterleave simulations with a bounded step budget per round (dovetailing)
Treating enumerators as requiring sorted/no-repeat outputThe definition only requires every string of LL to appear at some point; order and duplicates are irrelevantDefine L(E)L(E) as the set of strings ever printed, not a sequence
Assuming an enumerator for LL must haltFor infinite LL, EE must run forever to print infinitely many strings; halting is only expected/optional for finite LLOnly require completeness of the (possibly infinite) output set
Confusing "encodable" with "decidable"Every finite object over a finite alphabet is trivially encodable; encodability says nothing about whether membership questions about it are decidableKeep encoding (a syntactic bookkeeping step) separate from decidability (a semantic computability property)
Assuming a decider built from an enumerator halts on all inputsThe enumerator-to-recognizer construction only yields a recognizer: it may loop forever on w∉Lw \notin LTo get a decider, you need LL and its complement each enumerable (co-recognizable), by a separate theorem

Pioneers & history

The encoding of machines as data, and the enumerator/recognizer duality, trace back to the founding of computability theory in the 1930s, when the very notion of an "algorithm" had to be pinned down mathematically before anyone could ask what algorithms cannot do.

Origins · 1 minAlan Turing and how this was discovered
AT
Alan Turing
1912–1954 · Introduced Turing machines and the encoding of a machine's description as data fed to a "universal machine," the direct ancestor of encoding schemes ⟨M⟩\langle M \rangle used throughout computability theory.

History
1936
— Turing's "On Computable Numbers" introduces machine descriptions as data and the universal machine.
1936
— Church's lambda calculus gives an equivalent, encoding-free formalization, reinforcing the Church-Turing Thesis.
1943–1944
— Post's work on canonical/normal systems formalizes generative, enumeration-style definitions of formal languages.
1950
s — Kleene's recursion theorem and enumeration theorems formalize "recursively enumerable" sets via explicit listing machines.
1960
s–1970s — Textbook treatments (Hopcroft–Ullman, and later Sipser) standardize the recognizer/enumerator equivalence as core curriculum.

Where this matters

Encoding schemes are the invisible plumbing behind every system where programs treat other programs as data: compilers parse source code (an encoding of a program) into an abstract syntax tree; virtual machines like the JVM load bytecode (an encoding of a program for a specific machine model) and execute it; version-control and build systems serialize dependency graphs and configuration objects as strings for storage and transmission. The very idea of a Universal Turing Machine — one machine that simulates any other machine given as encoded input — is only meaningful because a robust, decodable encoding convention exists; this is the theoretical seed of every general-purpose computer, which is itself a physical universal machine executing encoded programs. Enumerators show up wherever systems must generate all instances of something without ever declaring "done": web crawlers enumerate reachable pages, theorem provers enumerate consequences of an axiom set, and property-based testing tools enumerate candidate inputs. The equivalence between recognizing and enumerating clarifies a subtle limitation these systems share: a search process that can verify a "success" case is a recognizer, and it can always be turned into a generator of successes by dovetailing — but it can never be upgraded into a machine that also reliably reports failure (halts and rejects) unless extra structure (co-recognizability) is available. This distinction is the direct ancestor of the harder undecidability results explored under Decidable Problems About Automata, where recognizing an automaton property is easy but deciding it, or its negation, can be genuinely impossible.

Ask the tutor: How does the dovetailing idea generalize to build a Universal Turing Machine that simulates any encoded machine on any encoded input?

Why can't we just say a Turing machine "reads" a graph or another machine directly?

Turing machines are defined to operate on strings over a finite tape alphabet — that is the entire input/output interface the model provides. A graph, automaton, or another machine is a richer mathematical structure (sets, relations, transition tables) that has no tape representation on its own. Encoding supplies the missing bridge: a fixed, effective, decodable procedure that serializes the structure into a string ⟨O⟩\langle O \rangle, so a Turing machine can legitimately "read" it by reading that string and running a decoding subroutine first. Without this step, statements like "a Turing machine takes another Turing machine as input" would be meaningless; with it, they become precise and provable, which is exactly what underlies the construction of a Universal Turing Machine.

Does the specific choice of encoding scheme matter for the theorems?

No, as long as the scheme is effective, injective, and decodable. Any two such schemes for the same class of objects are computably intertranslatable: a machine can read an object encoded under scheme A and output its encoding under scheme B, and vice versa. Since decidability and recognizability are closed under this kind of computable pre/post-processing, no result about whether a language is decidable or recognizable can flip depending on which reasonable encoding you picked. This is why textbooks state theorems using generic angle-bracket notation ⟨M⟩\langle M \rangle without ever pinning down the exact bit layout — the layout is a free implementation choice, not a mathematical commitment.

Is every enumerable language automatically decidable?

No. Enumerability is exactly equivalent to Turing-recognizability, which is strictly weaker than decidability in general. A language LL is decidable exactly when both LL and its complement are enumerable (recognizable); if only LL is enumerable but its complement is not, LL is recognizable but undecidable. The classic example is the acceptance problem ATM={⟨M,w⟩:M accepts w}A_{TM} = \{\langle M, w\rangle : M \text{ accepts } w\}: it is enumerable (dovetail-simulate every machine on every input and print accepting pairs) but not decidable, because no algorithm can always correctly detect non-acceptance in finite time.

Why does the dovetailing construction sometimes print the same string more than once?

Because different (p, q) pairs can all detect the same accept: once M accepts w_p within q0 steps, it also "accepts within q steps" for every q ≥ q0, so every later round that revisits index p will re-detect and re-print w_p. The formal definition of the language enumerated by E, L(E), is defined as a set of strings that appear at some point, so duplicate printings are harmless — they don't change which set is enumerated. If a duplicate-free enumerator is wanted for some downstream use, one simply adds a memory table of already-printed strings and skips reprinting, which is easy but not required by the definition.

How does this connect to the phrase "recursively enumerable" used in older textbooks?

"Recursively enumerable" (r.e.) is an older name, from before the standardization of "Turing-recognizable," for exactly the same class of languages: those for which some algorithm can list all and only their members. The name reflects the historical framing via general recursive functions rather than Turing machines, but the Church-Turing Thesis guarantees these formalizations define the same class. The Enumerator-Recognizer Equivalence Theorem is precisely the statement that justifies using "recursively enumerable" and "Turing-recognizable" interchangeably: a language is listable by some enumerating procedure exactly when a Turing machine can recognize (accept members of, possibly loop on non-members) it.

Can an enumerator enumerate the empty language?

Yes — an enumerator for L=∅L = \varnothing simply never prints anything, for example a machine that immediately enters an infinite loop without ever invoking its printer. This is consistent with the definition: L(E)L(E) is the set of strings ever printed, and the empty set of printed strings is a perfectly valid (if unexciting) output. Correspondingly, the trivial recognizer that rejects (loops on) every input recognizes ∅\varnothing, confirming the equivalence holds even at this degenerate boundary case.

Before this

Where this leads