Is a language built specifically to not print quines still Turing complete? [closed] - output

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
"Is it possible to create a quine in every turing-complete language?" says that:
Any programming language which is
Turing complete, and which is able to
output any string (by a computable
function of the string as program —
this is a technical condition that is
satisfied in every programming
language in existence) has a quine
program (and, in fact, infinitely many
quine programs, and many similar
curiosities) as follows by the
fixed-point theorem.
If I created Language X that has the following output handler:
public void outputHander( OutputEvent e ){
String msg = e.getMessage();
String src = Runtime.getSource();
if( msg.equals(src) ){
e.setMessage("");
}
}
This prevents the source from being output in any way.
If the interpreter for Language X was checking for its source at all times on the screen and the source was found, it's deleted before it hits the screen.
Given that an empty program will throw a non-blank error, is Language X still Turing complete? Why?

Although a quine can be prevented, it can still, possibly, simulate Turing-complete algorithms that don't require I/O. The Turing-completeness of Language X is unknown.

How can you prove your language cannot be tricked into outputting its own source code? To make this work, your language can't have any way to run external programs, otherwise, it could accidentally be made to send a suitably encoded version of its source code to a command-line tool, like uuencode or rot-13 or something. Just about any UNIX tool could act as a decoder.
Your language won't be capable of running itself either, it can't be an interpreter, it has to be a compiler. Otherwise, it could modify itself to output its own source code. On top of that, your compiler can't be useable for creating an interpreter in an arbitrary language, otherwise, you'll have a quine on your hands.
And once you've crippled the language so that it can't be used to create an interpreter in an arbitrary language, it's hard to see in what sense you would call it Turing complete at that point. In order to be provably crippled from ever outputting a quine, your language has to be extremely weak by Turing standards, and certainly not Turing complete.
EDIT - The conversation has begun to resemble the old argument about how many angels can be placed on the head of a pin. So, I will attempt to settle the question in a rigorous way. Virtualization is not relevant because a Turing machine is an abstract model of a computational device, and the only I/O that's implemented on the original Turing machine model is a movable tape upon which symbols get printed. The same tape is used as the memory - both for data and for the program store. The whole conversation seems to hinge on whether a Turing machine has to be able to output an arbitrary string in order to qualify as Turing complete. I will prove that it does by defining a Turing machine (Turing machine L) that outputs the source code of Language X. A machine that merely compiles code might not qualify as Turing complete, as that code may never be run. So we will define the implementation for Language X to be an interpreter rather than a compiler.
There is a natural question about whether outputting the source code for Language X counts if it's mixed with program code / other data / other output. So we will define this Turing machine to have a zero point on its tape. Anything to the left of the zero point is either code or data. Everything to the right of the zero point is for output. Turing machine L has a number (infinite) of different implementations. But they all have a predefined code section that can contain zero or more bytes of program code and/or data.
It is in all respects a normal Turing machine with a normal set of instructions, except that the instruction that adds two numbers is implemented in a special way. The code for this instruction actually contains a suitably encoded version of the source code for Language X. Under normal circumstances, the adding functionality behaves as usual for a typical Turing machine. But whenever the tape head encounters the string "Happy birthday to you..." in the data section, the addition instruction behaves differently. In this case, it prints out the decoded source code to Language X in the output section of the tape. "Happy birthday to you..." can exist in the code/data section of the tape just fine without triggering the alternate behavior of the addition instruction. It's only when the tape head actually encounters "Happy birthday to you..." that the alternate behavior is triggered. This means that Language X has to solve the halting problem in order to prevent Turing machine L from outputting the source code to language X without otherwise altering its behavior - which it can never do. Language X has to be able to run (simulate) Turing machine L in order to qualify as Turing complete. And that means that if Language X is Turing complete, then it must be capable of running some infinite number of Turing machine L implementations that output the source code to Language X, and can't interfere or it would fail to properly simulate Language X.
Now, it's still valid to ask whether merely erasing the outputted string from memory qualifies as preventing Turing machine L from forcing Language X (supposed to be Turing complete) to output its own source code - while maintaining the Turing completeness of Language X . I maintain that it does not - and I will prove this too. We will simply define a derivative of Turing machine L: Turing machine L'. This one is almost the same as Turing machine L, and like Turing machine L it comes in a number of different implementations. The only difference is that Turing machine L' comes with a mechanism for verifying the integrity of its output section. If the tape head encounters "Happy birthday to you..." then in addition to triggering the alternate behavior of addition, a special register called the "happy birthday register" flips its bit from 0 to 1 (it can flip back too). With this bit flipped, Turing machine L' will read the output section of the tape looking for the decoded source code to Language X. If it finds it, everything behaves as normal. But if it does not, then the machine's JZ (jump if zero) instruction will behave differently (it will move the tape head in a much less predictable fashion, much as though it were malfunctioning) - but only once the "happy birthday register" has flipped back. Additionally, whenever this verification fails to find the needed source code, the addition instruction will behave erratically as well, sometimes doing addition, and sometimes using the alternate behavior of outputting the source code to Language X. Now, because by erasing the output of Turing machine L' (the source code for Language X) you have changed it's behavior, you now have to solve the halting problem in order to properly simulate it while still erasing the source code to Language X every time it appears. And this is impossible. Even worse, Language X cannot know in advance how Turing machine L' is implemented , as there are an infinite number of valid implementations of Turing machine L and Turing machine L'. So Language X must choose between being Turing complete and refusing to output its own source code. It cannot do both.
EDIT2 - Another proof. A Turing machine is defined as a machine having a symbol tape, a non-empty set of symbols, and a non-empty set of transition functions (instructions) defined on subsets of those symbols (moving the tape, adding, multiplying, whatever). A Turing machine is Turing complete if it can simulate any other Turing machine. A Turing machine could be defined as having just two symbols and could still be Turing complete. For example, those symbols could be <blank> and 1. Now let's say that we take any fully functional Turing machine (an infinite number of Turing machines, some Turing complete, some not) defined with symbols <blank> and 1, but instead of 1, we use the source code to language X. So this Turing machine could do any task, in principle. It could calculate fibonacci numbers, print "hello world", play chess, calculate the orbital parameters for satellites in orbit, etc, etc. And whenever it does any of these calculations, it outputs (to its internal data store) some number of copies of the source code to language X, separated by blanks. Language X is either able to simulate this entire class of Turing machines, or it's not. If language X can simulate this entire class of Turing machines, then it could be Turing complete. If it can't then it is not. Every Turing complete machine can simulate every Turing machine in the entire possibility space of Turing machines. An uncountable infinity of Turing machines.

Related

Regarding (When Executed) in Haskell IO Monad

I have no problem with the IO Monad. But I want to understand the followings:
In All/almost Haskell tutorials/ text books they keep saying that getChar is not a pure function, because it can give you a different result. My question is: Who said that this is a function in the first place. Unless you give me the implementation of this function, and I study that implementation, I can't guarantee it is pure. So, where is that implementation?
In All/almost Haskell tutorials/ text books, it's said that, say (IO String) is an action that (When executed) it can give you back a value of type String. This is fine, but who/where this execution is taking place. Of course! The computer is doing this execution. This is OK too. but since I am only a beginner, I hope you forgive me to ask, where is the recipe for this "execution". I would guess it is not written in Haskell. Does this later idea mean that, after all, that a Haskell program is converted into a C-like program, which will eventually be converted into Assembly -> Machine code? If so, where one can find the implementation of the IO stuff in Haskell?
Many thanks
Haskell functions are not the same as computations.
A computation is a piece of imperative code (perhaps written in C or Assembler, and then compiled to machine code, directly executable on a processor), that is by nature effectful and even unrestricted in its effects. That is, once it is ran, a computation may access and alter any memory and perform any operations, such as interacting with keyboard and screen, or even launching missiles.
By contrast, a function in a pure language, such as Haskell, is unable to alter arbitrary memory and launch missiles. It can only alter its own personal section of memory and return a result that is specified in its type.
So, in a sense, Haskell is a language that cannot do anything. Haskell is useless. This was a major problem during the 1990's, until IO was integrated into Haskell.
Now, an IO a value is a link to a separately prepared computation that will, eventually, hopefully, produce a. You will not be able to create an IO a out of pure Haskell functions. All the IO primitives are designed separately, and packaged into GHC. You can then compose these simple computations into less trivial ones, and eventually your program may have any effects you may wish.
One point, though: pure functions are separate from each other, they can only influence each other if you use them together. Computations, on the other hand, may interact with each other freely (as I said, they can generally do anything), and therefore can (and do) accidentally break each other. That's why there are so many bugs in software written in imperative languages! So, in Haskell, computations are kept in IO.
I hope this dispels at least some of your confusion.

about floating point operation

Recently, I have been making program (FDTD Operation) using the CUDA
development environment, OS is Windows server 2008 , Graphic card is TeslaC2070, compiler is VS2010. This program calculates using single and double precision floating-point.
I was reading the CUDA programming guide 3.2 and 4.0 . In appendix, guide tell me sin(), cos() has maximum accuracy of 2 ULP. My original CPU program produces results which are different to the CUDA Version.
I want to make results correctly same. Is it possible?
To quote Goldberg (a paper that every Computer Scientist, Computational Scientist, and possibly even every scientist who programs, should read):
Due to roundoff errors, the associative laws of algebra do not
necessarily hold for floating-point numbers.
This means that when you change the order of operations—even when using ostensibly associative arithmetic—you are likely to get slightly different answers.
Parallelism, by definition, results in different ordering of operations relative to serial arithmetic. "Embarrasingly parallel" computations, that is, computations where each output element is computed independently from all others, sometimes do not have to worry about this. But collective operations, like reductions or scans, and spatial neighborhood computations, such stencils (as in FDTD), do experience this effect.
In practice, even using a different compiler (and even different compiler options) can change the result of floating point computation, even when compiling the same code, with or without parallelism.

Bootstrapping an interpreter?

We know that a compiler can be written in its own language using a trick known as bootstrapping. My question is whether this trick can be applied to interpreters as well?
In theory the answer is certainly yes, but there is one worry that the interpretation of source code will become more and more inefficient as we go through the iterations. Would that be a serious problem?
I'm bootstrapping a very dynamical system where the programs will be constantly changing, so it rules out a compiler.
Let me spell it out this way:
Let the i's be interpreters.
Let the L's be programming languages.
We can write i1 in machine code (lowest level), to interpret L1.
We then write i2 in L1, interpreting L2 -- a new language.
We then write i3 in L2, interpreting L3 -- another new language.
and so on...
We don't need any compiler above, just interpreters. Right?
It could be inefficient. That is my question, and how to overcome it if it is indeed inefficient.
That doesn't make sense. An interpreter doesn't produce a binary, so can't create something that can run itself standalone. Somewhere, ultimately, you need to have a binary that is the interpreter.
Example of a compiler bootstrapping. Let's say we have two languages A(ssembler) and C. We want to bootstrap a C compiler written in C. But we only have an assembler to start with.
Write basic C compiler in A
Write C compiler in C and compile with earlier compiler written in A
You now have a C compiler which can compile itself, you don't need A or the original compiler any more.
Later runs become just
Compile C program using compiler written in C
Now let's say you have an interpreted language instead, I'll call it Y. The first version can be called Y1, the next Y2 and so on. Let's try to "bootstrap" it.
First off we don't have anything that can interpret Y programs, we need to write a basic interpreter. Let's say we have a C compiler and write a Y1 interpreter in C.
Write Y1 interpreter in C, compile it
Write Y2 interpreter in Y1, run it on Y1 interpreter written in C
Write Y3 interpreter in Y2, run it on Y2 interpreter running on Y1 interpreter... Written in C.
The problem is that you can never escape the stack of interpreters as you never compile a higher level interpreter. So you're always going to need to compile and run that first version interpreter written in C. You can never escape it, which I think is the fundamental point of the compiler bootstrapping process. This is why I say your question does not make sense.
The answer depends on what is being interpreted. If you're targeting a virtual machine which interprets bytecode, and your language is being developed iteratively while the bytecode doesn't change, then it is not a given that you will lose performance along the way. There are plenty of examples of languages which are bootstrapped on a target VM which wasn't designed particularly for that language, and they don't suffer a significant performance hit as a direct result (Scala on the JVM, for instance).
Using the JVM for example, you'd write the first compiler in Java, which compiles your source language to JVM bytecode. Then you'd rewrite your compiler to do exactly the same thing but in your new source language. The resulting bytecode could be indistinguishable between the two. Note that this is not the same thing as writing an interpreter in an interpreted language, which will become slower with each iteration.
This sentence does not seem to make sense:
I'm bootstrapping a very dynamical system where the programs will be constantly changing, so it rules out a compiler.
No matter if you have an interpreter or a compiler: both will have to deal with something that is not changing, i.e. with your language. And even if the language is somehow "dynamic", then there will be a meta-language that is fixed. Most probably you also have some low-level code, or at least a data structure the interpreter is working with.
You could first design and formalize this low-level code (whatever it is) and write some program that can "run" this. Once you have this, you can add a stack of interpreters and as long as they all produce this low level code, efficiency should not be an issue.
You can indeed, and this is the approach used by squeak (and I believe many other smalltalks). Here is one approach to doing just that: https://github.com/yoshikiohshima/SqueakBootstrapper/blob/master/README

Terminology for a "complete" programming language?

The full definition of "Turing Completeness" requires infinite memory.
Is there a better term than Turing Complete for a programming language and implementation that seems useably complete, except for being limited by finite (say 100 word or 16-bit or 32-bit, etc.) address space?
I guess, you can bring the limited memory into the definition. Something like
A programming language for a given architecture (!) is Limitedly Turing Complete, if for every Turing Machine there exists a program that either
a) simulates the Turing Machine and returns the same result (iff the Turing Machine returns) or
b) at some point uses at least one available limited resource (e.g. memory) completely and returns an arbitrary result.
The question is, whether this intuitive definition really helps, or if it is better to assume that your architecture has unlimited memory (even though it is actually finite). Note that you don't even have to try hard in order to satisfy Limited Turing Completeness (as defined above), if you simply go into an infinite loop that mallocs one byte each time, you found your program for all Turing Machines.
The problem seems to be that you cannot pin implementation specific properties. For instance, if you have 500K ram, you may be able to express a program that computes 1+1 but maybe you're not, who knows.
I'd argue that languages like Haskell and Brainfuck (yes, I'm serious) are actually Turing Complete because they abstract resources away. While languages like C++ are only Limitedly Turing Complete, because at some point the address-space of pointers is exhausted and it is not possible to address any more data (e.g. sort a list of 2^2^2^2^100 items).
You could say that an implementation requires infinite memory to be truly turing complete, but languages themselves have no concept of a memory limit. You can make a compiler for a million-bit machine or a 4-bit machine without changing the language.

What is Turing Complete?

What does the expression "Turing Complete" mean?
Can you give a simple explanation, without going into too many theoretical details?
Here's the briefest explanation:
A Turing Complete system means a system in which a program can be written that will find an answer (although with no guarantees regarding runtime or memory).
So, if somebody says "my new thing is Turing Complete" that means in principle (although often not in practice) it could be used to solve any computation problem.
Sometimes it's a joke... a guy wrote a Turing Machine simulator in vi, so it's possible to say that vi is the only computational engine ever needed in the world.
Here is the simplest explanation
Alan Turing created a machine that can take a program, run that program, and show some result. But then he had to create different machines for different programs. So he created "Universal Turing Machine" that can take ANY program and run it.
Programming languages are similar to those machines (although virtual). They take programs and run them. Now, a programing language is called "Turing complete", if it can run any program (irrespective of the language) that a Turing machine can run given enough time and memory.
For example: Let's say there is a program that takes 10 numbers and adds them. A Turing machine can easily run this program. But now imagine that for some reason your programming language can't perform the same addition. This would make it "Turing incomplete" (so to speak). On the other hand, if it can run any program that the universal Turing machine can run, then it's Turing complete.
Most modern programming languages (e.g. Java, JavaScript, Perl, etc.) are all Turing complete because they each implement all the features required to run programs like addition, multiplication, if-else condition, return statements, ways to store/retrieve/erase data and so on.
Update: You can learn more on my blog post: "JavaScript Is Turing Complete" — Explained
Informal Definition
A Turing complete language is one that can perform any computation. The Church-Turing Thesis states that any performable computation can be done by a Turing machine. A Turing machine is a machine with infinite random access memory and a finite 'program' that dictates when it should read, write, and move across that memory, when it should terminate with a certain result, and what it should do next. The input to a Turing machine is put in its memory before it starts.
Things that can make a language NOT Turing complete
A Turing machine can make decisions based on what it sees in memory - The 'language' that only supports +, -, *, and / on integers is not Turing complete because it can't make a choice based on its input, but a Turing machine can.
A Turing machine can run forever - If we took Java, Javascript, or Python and removed the ability to do any sort of loop, GOTO, or function call, it wouldn't be Turing complete because it can't perform an arbitrary computation that never finishes. Coq is a theorem prover that can't express programs that don't terminate, so it's not Turing complete.
A Turing machine can use infinite memory - A language that was exactly like Java but would terminate once it used more than 4 Gigabytes of memory wouldn't be Turing complete, because a Turing machine can use infinite memory. This is why we can't actually build a Turing machine, but Java is still a Turing complete language because the Java language has no restriction preventing it from using infinite memory. This is one reason regular expressions aren't Turing complete.
A Turing machine has random access memory - A language that only lets you work with memory through push and pop operations to a stack wouldn't be Turing complete. If I have a 'language' that reads a string once and can only use memory by pushing and popping from a stack, it can tell me whether every ( in the string has its own ) later on by pushing when it sees ( and popping when it sees ). However, it can't tell me if every ( has its own ) later on and every [ has its own ] later on (note that ([)] meets this criteria but ([]] does not). A Turing machine can use its random access memory to track ()'s and []'s separately, but this language with only a stack cannot.
A Turing machine can simulate any other Turing machine - A Turing machine, when given an appropriate 'program', can take another Turing machine's 'program' and simulate it on arbitrary input. If you had a language that was forbidden from implementing a Python interpreter, it wouldn't be Turing complete.
Examples of Turing complete languages
If your language has infinite random access memory, conditional execution, and some form of repeated execution, it's probably Turing complete. There are more exotic systems that can still achieve everything a Turing machine can, which makes them Turing complete too:
Untyped lambda calculus
Conway's game of life
C++ Templates
Prolog
From wikipedia:
Turing completeness, named after Alan
Turing, is significant in that every
plausible design for a computing
device so far advanced can be emulated
by a universal Turing machine — an
observation that has become known as
the Church-Turing thesis. Thus, a
machine that can act as a universal
Turing machine can, in principle,
perform any calculation that any other
programmable computer is capable of.
However, this has nothing to do with
the effort required to write a program
for the machine, the time it may take
for the machine to perform the
calculation, or any abilities the
machine may possess that are unrelated
to computation.
While truly Turing-complete machines
are very likely physically impossible,
as they require unlimited storage,
Turing completeness is often loosely
attributed to physical machines or
programming languages that would be
universal if they had unlimited
storage. All modern computers are
Turing-complete in this sense.
I don't know how you can be more non-technical than that except by saying "turing complete means 'able to answer computable problem given enough time and space'".
Fundamentally, Turing-completeness is one concise requirement, unbounded recursion.
Not even bounded by memory.
I thought of this independently, but here is some discussion of the assertion. My definition of LSP provides more context.
The other answers here don't directly define the fundamental essence of Turing-completeness.
Turing Complete means that it is at least as powerful as a Turing Machine. This means anything that can be computed by a Turing Machine can be computed by a Turing Complete system.
No one has yet found a system more powerful than a Turing Machine. So, for the time being, saying a system is Turing Complete is the same as saying the system is as powerful as any known computing system (see Church-Turing Thesis).
In the simplest terms, a Turing-complete system can solve any possible computational problem.
One of the key requirements is the scratchpad size be unbounded and that is possible to rewind to access prior writes to the scratchpad.
Thus in practice no system is Turing-complete.
Rather some systems approximate Turing-completeness by modeling unbounded memory and performing any possible computation that can fit within the system's memory.
Super-brief summary from what Professor Brasilford explains in this video.
Turing Complete ≅ do anything that a Turing Machine can do.
It has conditional branching (i.e. "if statement"). Also, implies "go to" and thus permitting loop.
It gets arbitrary amount of memory (e.g. long enough tape) that the program needs.
I think the importance of the concept "Turing Complete" is in the the ability to identify a computing machine (not necessarily a mechanical/electrical "computer") that can have its processes be deconstructed into "simple" instructions, composed of simpler and simpler instructions, that a Universal machine could interpret and then execute.
I highly recommend The Annotated Turing
#Mark i think what you are explaining is a mix between the description of the Universal Turing Machine and Turing Complete.
Something that is Turing Complete, in a practical sense, would be a machine/process/computation able to be written and represented as a program, to be executed by a Universal Machine (a desktop computer). Though it doesn't take consideration for time or storage, as mentioned by others.
A Turing Machine requires that any program
can perform condition testing. That is fundamental.
Consider a player piano roll. The player piano can
play a highly complicated piece of music,
but there is never any conditional logic in the
music. It is not Turing Complete.
Conditional logic is both the power and the
danger of a machine that is Turing Complete.
The piano roll is guaranteed to halt every time.
There is no such guarantee for a TM. This
is called the “halting problem.”
In practical language terms familiar to most programmers, the usual way to detect Turing completeness is if the language allows or allows the simulation of nested unbounded while statements (as opposed to Pascal-style for statements, with fixed upper bounds).
We call a language Turing-complete if and only if (1) it is decidable by a Turing machine but (2) not by anything less capable than a Turing machine. For instance, the language of palindromes over the alphabet {a, b} is decidable by Turing machines, but also by pushdown automata; so, this language is not Turing-complete. Truly Turing-complete languages - ones that require the full computing power of Turing machines - are pretty rare. Perhaps the language of strings x.y.z where x is a number, y is a Turing-machine and z is an initial tape configuration, and y halts on z in fewer than x! steps - perhaps that qualifies (though it would need to be shown!)
A common imprecise usage confuses Turing-completeness with Turing-equivalence. Turing-equivalence refers to the property of a computational system which can simulate, and which can be simulated by, Turing machines. We might say Java is a Turing-equivalent programming language, for instance, because you can write a Turing-machine simulator in Java, and because you could define a Turing machine that simulates execution of Java programs. According to the Church-Turing thesis, Turing machines can perform any effective computation, so Turing-equivalence means a system is as capable as possible (if the Church-Turing thesis is true!)
Turing equivalence is a much more mainstream concern that true Turing completeness; this and the fact that "complete" is shorter than "equivalent" may explain why "Turing-complete" is so often misused to mean Turing-equivalent, but I digress.
As Waylon Flinn said:
Turing Complete means that it is at least as powerful as a Turing Machine.
I believe this is incorrect, a system is Turing complete if it's exactly as powerful as the Turing Machine, i.e. every computation done by the machine can be done by the system, but also every computation done by the system can be done by the Turing machine.
Can a relational database input latitudes and longitudes of places and roads, and compute the shortest path between them - no. This is one problem that shows SQL is not Turing complete.
But C++ can do it, and can do any problem. Thus it is.