26
5
The goal of this challenge is to (eventually) output every possible halting program in a language of your choice. At first this may sound impossible, but you can accomplish this with a very careful choice of execution order.
Below is an ASCII diagram to illustrate this. Let the columns represent a numbering of every possible program (each program is a finite number of symbols from a finite alphabet). Let each row represent a singular step in execution of that program. An X
represent the execution performed by that program at that time-step.
step# p1 p2 p3 p4 p5 p6
1 X X X X X X
2 X X X X X
3 X X X X
4 X X X X
5 X X X
6 X X
7 X X
8 X X
9 X X
∞ X X
As you can tell, programs 2 and 4 don't halt. If you were to execute them one-at-a-time, your controller would get stuck in the infinite loop that is program 2 and never output programs 3 and beyond.
Instead, you use a dovetailing approach. The letters represent a possible order of execution for the first 26 steps. The *
s are places where that program has halted and is outputted. The .
s are steps that haven't been executed yet.
step# p1 p2 p3 p4 p5 p6
1 A C F J N R V
2 B E I M Q * Z
3 D H * P U
4 G L T Y
5 K O X
6 * S .
7 W .
8 . .
9 . .
∞ . .
Requirements for the target language
The target language (the one being parallel-interpreted) must be Turing-complete. Other than that, it can be any language that's Turing-complete, including Turing-complete subsets of much larger languages. You are also free to interpret things like cyclic tag system rules. You are also allowed to create a language to test, as long as you can show why it is Turing-complete.
As an example, if you choose to test brainfuck, then it is best to test just the []-+<>
subset, since input is not supported and output is just thrown away (see below).
When it comes to the "controller" program (which you are golfing), there's no special requirements. Normal language restrictions apply.
How to create an infinite list of programs
The majority of programming languages can be represented as a series of symbols from a finite alphabet. In this case, it is relatively easy to enumerate a list of every possible program in order of increasing length. The alphabet you use should be representative of the requirements of the target language. In most cases, this is printable ASCII. If your language supports Unicode as an additional feature, you should not test every possible combination of Unicode characters, just ASCII. If your language only uses []-+<>
then don't test out the various combinations of "comment" ASCII characters. Languages like APL would have their own special alphabets.
If your language is best described in some non-alphabetic way, like Fractran or Turing Machines, then there are other equally valid methods of generating a list of all possible valid programs.
Interpreting an ever-growing list of programs
The key part of this challenge is to write a parallel interpreter for a growing list of programs. There are some basic steps for this:
- Add a finite number of programs to the list
- Interpret each program on the list individually for a finite period of time. This can be accomplished by performing one instruction step for each. Save all of the states.
- Remove all terminating/error-throwing programs from the list
- Output the cleanly halted* programs
- Add some more programs to the list
- Simulate each program in turn, picking up execution of older programs where it left off
- Remove all terminating/error-throwing programs from the list
- Output the cleanly halted* programs
- repeat
*You should only output programs that halt cleanly. This means that there were no syntax errors or uncaught exceptions thrown during execution. Programs which ask for input should also be terminated without outputting them. If a program produces output, you shouldn't terminate it, just throw the output away.
More rules
- You must not spawn new threads to contain the tested programs, as this offloads the work of parallelization onto the host OS / other software.
- Edit: To close potential future loopholes, you are not allowed to
eval
(or any related function) a part of the tested program's code. You caneval
a codeblock from the interpreter's code. (The BF-in-Python answer is still valid under these rules.) - This is code-golf
- The language you write your submission in does not need to be the same as the language you are testing/outputting.
- You should assume that your available memory is unbounded.
- When proving Turing-completeness, you may assume that input is hardcoded into the program, and output can be read off the program's internal state.
- If your program outputs itself, it is probably wrong or a polyglot.
7It took me far too long to realise why
"If your program outputs itself, it is probably wrong or a polyglot."
– trichoplax – 2015-06-04T15:18:01.5571May we assume that the memory available is unbounded (I don't think this is possible otherwise) – KSab – 2015-06-04T15:45:44.820
1@KSab Yes, and it definitely isn't possible otherwise. – PhiNotPi – 2015-06-04T16:09:49.053
For each program, should we assume that there is no input to the program? – frederick – 2015-06-04T18:11:14.033
@frederick Correct. – PhiNotPi – 2015-06-04T18:14:30.793
Turing completeness technically requires unlimited storage, doesn't it? Are there limits on what reasonable bounds we can place on the storage capacity of our target language, such as the array size for brainfuck? Or must we implement theoretically unlimited storage? – Sparr – 2015-06-05T03:58:36.353
You aren't allowed to use built-in multithreading? Great, now I have to completely restructure my answer. :( – SuperJedi224 – 2015-06-06T15:33:23.933
1Follow-up challenge (much harder): Output every non-halting program. – Milo Brandt – 2015-06-07T03:05:53.300
@MiloBrandt not "much harder", but actually impossible (for turing-complete languages). – Paŭlo Ebermann – 2015-12-02T17:04:00.897
Stupid loophole that should probably be closed: some languages are incapable of halting, and some of those are Turing-complete. (Alternatively, there are languages like Fractran which are capable of halting, but where nontrivial input is required for the halt test to be nontrivial; on the simplest possible input,
1
, it's provable that a program either halts immediately or not at all, with Turing-completeness requiring more complex input.) I think you can see where this is going. – None – 2017-02-27T08:38:53.0601Is it acceptable to output the same program more than once? – None – 2017-06-07T09:54:38.237
If a program produces output, you shouldn't terminate it, just throw the output away.
Why not if the language can choose to not output? It's still turing complete – l4m2 – 2018-04-13T12:08:32.370