|
| 1 | +# Turing Completeness |
| 2 | + |
| 3 | +Rust's Declarative Macros are Turing-Complete in their expansion. Here we will prove this fact. |
| 4 | + |
| 5 | +## Tag Systems |
| 6 | + |
| 7 | +A [Tag System](https://en.wikipedia.org/wiki/Tag_system) is a model of computation that we will use in our proof. |
| 8 | + |
| 9 | +### Definition |
| 10 | + |
| 11 | +> **Note:** the definition below is taken verbatim from Wikipedia. |
| 12 | +
|
| 13 | +Briefly, a Tag System is a triplet \\( (m, A, P) \\) where: |
| 14 | +\\( \\) |
| 15 | +* \\( m \\) is a positive integer, called the deletion number. |
| 16 | +* \\( A \\) is a finite alphabet of symbols, one of which can be a special *halting symbol* \\( H \\). All finite (possibly empty) strings on \\( A \\) are called **words**. |
| 17 | +* \\( P \\) is a set of production rules, assigning a word \\( P(x) \\) (called a production) to each symbol \\( x \\) in \\( A \\). The production (say \\( P(H) \\)) assigned to the halting symbol is seen below to play no role in computations, but for convenience is taken to be \\( P(H) = H \\). |
| 18 | + |
| 19 | +A **halting word** is a word that either: |
| 20 | +* Begins with the halting symbol \\( H \\), |
| 21 | +* or whose length is less than \\( m \\). |
| 22 | + |
| 23 | +A **transformation** \\( t \\) (called the tag operation) is defined on the set of non-halting words, such that if \\( x \\) denotes the leftmost symbol of a word \\( S \\), then \\( t(S) \\) is the result of deleting the leftmost \\( m \\) symbols of \\( S \\) and appending the word \\( P(x) \\) on the right. Thus, the system processes the m-symbol head into a tail of variable length, but the generated tail depends solely on the first symbol of the head. |
| 24 | + |
| 25 | +A **computation** by a tag system is a finite sequence of words produced by iterating the transformation \\( t \\), starting with an initially given word and halting when a halting word is produced. |
| 26 | + |
| 27 | +### Computational Power |
| 28 | + |
| 29 | +When its parameter \\( m \\) is greater than 1, a Tag System is capable of representing an arbitrary Turing Machine. We will use this fact in our proof. |
| 30 | + |
| 31 | +## Proof |
| 32 | + |
| 33 | +The proof consists of the following steps: |
| 34 | + |
| 35 | +1. We will take an **arbitrary** Tag System \\( T \\) of \\( m > 1 \\). |
| 36 | +2. We will construct a `macro_rules!` definition `my_tag_system` that simulates this Tag System, such that: |
| 37 | + 1. Every halting string in the Tag System halts in our macro. |
| 38 | + 2. Every production rule in the Tag System is reproduced exactly in our macro. |
| 39 | + |
| 40 | +This will prove that we can fully simulate any Tag System of \\( m > 1 \\) with a `macro_rules!` definition's expansion. And, since simulating a Tag System inherits its halting properties, our macro's expansion will halt **if and only if the Tag System halts**. |
| 41 | + |
| 42 | +### An Arbitrary Tag System |
| 43 | + |
| 44 | +Let's specify an arbitrary Tag System \\( T = (m, A, P) \\): |
| 45 | + |
| 46 | +* \\( m \in \mathbb{N}, m > 1 \\) |
| 47 | +* \\( A \\) is our set of symbols. |
| 48 | + * \\( A = \\{ a, b, c, \ldots \\} \\) |
| 49 | + * \\( A \\) is finite |
| 50 | + * The empty string is considered a string on \\( A \\). |
| 51 | + * \\( A \\) may contain a special character for halting: \\( H \\). |
| 52 | +* \\( P \\) is a set of production rules \\( \\{ p_a, p_b, p_c, \ldots \\} \\), which can be equivalently seen as a function from \\( A \\) to the strings on \\( A \\): |
| 53 | + |
| 54 | +\\[ P: A \to A^\star \\] |
| 55 | + |
| 56 | +### Constructing a Macro Definition for our Tag System |
| 57 | + |
| 58 | +We will now construct a `macro_rules!` definition that fully simulates `T`. |
| 59 | + |
| 60 | +#### Declaration |
| 61 | + |
| 62 | +We begin with the declaration. Nothing special here. |
| 63 | + |
| 64 | +```rust,ignore |
| 65 | +macro_rules! my_tag_system { |
| 66 | +``` |
| 67 | + |
| 68 | +#### Halting Input |
| 69 | + |
| 70 | +First we define what inputs will halt. There are two cases. |
| 71 | + |
| 72 | +A string beginning with the special character \\( H \\) will halt. |
| 73 | + |
| 74 | +```rust,ignore |
| 75 | +(H $($tail:tt)*) => {}; |
| 76 | +``` |
| 77 | + |
| 78 | +A string of length \\( [0 \ldots (m - 1)] \\) will halt. |
| 79 | + |
| 80 | +```rust,ignore |
| 81 | +() => {}; |
| 82 | +
|
| 83 | +($ident_1:ident) => {}; |
| 84 | +
|
| 85 | +($ident_1:ident $ident_2:ident) => {}; |
| 86 | +
|
| 87 | +/* ... */ |
| 88 | +
|
| 89 | +($ident_1:ident $ident_2:ident /* ... */ $ident_m_minus_2:ident) => {}; |
| 90 | +
|
| 91 | +($ident_1:ident $ident_2:ident /* ... */ $ident_m_minus_2:ident $ident_m_minus_1:ident) => {}; |
| 92 | +``` |
| 93 | + |
| 94 | +#### Non-Halting Input |
| 95 | + |
| 96 | +> **Note**: given two strings \\( a, b \\), the concatenation of both is written as \\( a | b \\) |
| 97 | +
|
| 98 | +For each symbol in \\( A \\), we have a production rule. To recap: |
| 99 | + |
| 100 | +A string \\( S = x | head | tail \\) with \\( \texttt{len}(head) = m - 1 \\) will be transformed into \\( t(S) \\) in the following way: |
| 101 | + |
| 102 | +\\[ t(S) \\] |
| 103 | + |
| 104 | +\\[ = t(x | head | tail) \\] |
| 105 | + |
| 106 | +\\[ = tail | P(x) \\] |
| 107 | + |
| 108 | +Thus, we write each of the rules in `P` like this: |
| 109 | + |
| 110 | +```rust,ignore |
| 111 | +// P(x) |
| 112 | +(x $d_1:tt $d_2:tt /* ... */ $d_m_minus_1:tt $($tail:tt)*) => { |
| 113 | + my_tag_system!($($tail)* p_x) |
| 114 | +}; |
| 115 | +``` |
| 116 | + |
| 117 | +Where we replace `p_x` with the expansion of \\( P(x) \\). |
| 118 | + |
| 119 | +#### All Together |
| 120 | + |
| 121 | +Putting it all together, it looks like this: |
| 122 | + |
| 123 | +```rust |
| 124 | +macro_rules! my_tag_system { |
| 125 | + |
| 126 | + // Halting Input |
| 127 | + (H $($tail:tt)*) => {}; |
| 128 | + |
| 129 | + () => {}; |
| 130 | + |
| 131 | + ($ident_1:ident) => {}; |
| 132 | + |
| 133 | + ($ident_1:ident $ident_2:ident) => {}; |
| 134 | + |
| 135 | + /* ... */ |
| 136 | + |
| 137 | + ($ident_1:ident $ident_2:ident /* ... */ $ident_m_minus_2:ident) => {}; |
| 138 | + |
| 139 | + ($ident_1:ident $ident_2:ident /* ... */ $ident_m_minus_2:ident $ident_m_minus_1:ident) => {}; |
| 140 | + |
| 141 | + // Non-Halting Input |
| 142 | + |
| 143 | + // P(a) |
| 144 | + (a $d_1:tt $d_2:tt /* ... */ $d_m_minus_1:tt $($tail:tt)*) => { |
| 145 | + my_tag_system!($($tail)* p_a) |
| 146 | + }; |
| 147 | + |
| 148 | + // P(b) |
| 149 | + (b $d_1:tt $d_2:tt /* ... */ $d_m_minus_1:tt $($tail:tt)*) => { |
| 150 | + my_tag_system!($($tail)* p_b) |
| 151 | + }; |
| 152 | + |
| 153 | + // ... |
| 154 | +} |
| 155 | +# |
| 156 | +# fn main() {} |
| 157 | +``` |
| 158 | + |
| 159 | +### Analysis of our Macro Definition |
| 160 | + |
| 161 | +When does our macro's expansion halt? |
| 162 | + |
| 163 | +Well, as we can see from our definition: |
| 164 | +* We can represent all strings on \\( A \\). |
| 165 | +* Our macro's definition halts with all strings for which \\( T \\) halts. |
| 166 | +* Our macro's definition reproduces all production rules present in \\( T \\). No more, and no less. |
| 167 | + |
| 168 | +Therefore, the macro expansion process will quite literally simulate the Tag System we've encoded in our definition. |
| 169 | + |
| 170 | +Our macro's expansion will halt **if and only if \\( T \\) halts**. |
| 171 | + |
| 172 | +#### Caveat: Recursion Limit |
| 173 | + |
| 174 | +The Rust Compiler has, very reasonably, a `recursion_limit` parameter for macro expansion. This allows the compiler to bail itself out in case a macro expansion is taking an unreasonably long time. |
| 175 | + |
| 176 | +Such a parameter does technically mean that our macros will always halt. But for the purposes of formal analysis, that's just an implementation detail. |
| 177 | + |
| 178 | +> **Note**: it's the same as noting that a computer's RAM is finite, and thus doesn't represent a real Turing Machine. |
| 179 | +
|
| 180 | +Besides, this limit can be bypassed by setting the `recursion_limit` attribute to a very large number, for example: `#![recursion_limit = "999999999999999999"]` |
| 181 | + |
| 182 | +### Relation to Turing-Completeness |
| 183 | + |
| 184 | +* A Tag System with \\( m > 1 \\) is Turing-Complete. |
| 185 | + * This means that we can take an arbitrary Turing Machine \\( M \\) and convert it into a Tag System \\( T \\) with \\( m > 1 \\) that halts if and only if \\( M \\) halts. |
| 186 | +* We have just proven that we can take an arbitrary Tag System \\( T \\) and convert it into a `macro_rules!` definition `my_tag_system` that halts in its expansion if and only if \\( T \\) halts. |
| 187 | + |
| 188 | +Therefore, we are able to: |
| 189 | +1. Take an arbitrary Turing Machine \\( M \\), |
| 190 | +2. Convert it into a Tag System \\( T \\) that halts if and only if \\( M \\) halts, |
| 191 | +3. And then convert it into a `macro_rules!` definition `my_tag_system` that halts in its expansion if and only if \\( T \\) halts. |
| 192 | + |
| 193 | +And since the "if and only if" relation is transitive, we've just proven that `macro_rules!` expansion is Turing-Complete. |
| 194 | + |
| 195 | +## Consequences of Turing-Completeness |
| 196 | + |
| 197 | +The fact that Rust's Declarative Macros are Turing Complete imposes very important consequences on tooling: |
| 198 | + |
| 199 | +* Will a macro always finish expanding? Undecidable. |
| 200 | +* By [Rice's Theorem](https://en.wikipedia.org/wiki/Rice%27s_theorem), most properties of a Turing-Complete system are Undecidable. |
| 201 | + * Are two macros equivalent? Who knows! It's actually very likely that this is impossible to tell in general. |
| 202 | + * Is a macro's definition arm redundant? Might be impossible to tell in general. |
| 203 | + |
| 204 | +All of this is to say: as far as the author of this fragment (Félix Fischer) is concerned, it's **To Be Determined** whether or not any of these questions and many more like them are even answerable. |
0 commit comments