Impossible Programs

Gregory M. Kapfhammer

September 8, 2025

Impossible programs?

  • The central question of uncomputability
    • Some problems cannot be solved by any program
    • Even infinite time and memory cannot solve them
    • This is one of the most profound results in computer science
  • Our exploration strategy
    • Start with proof by contradiction
    • Build programs that analyze other programs
    • Demonstrate fundamental impossibility results
    • Connect to practical software engineering limits

Proof by contradiction

  • Natural human reasoning pattern
    • Statement S: “I bought the milk on Monday”
    • Consequence C: “I was in town on Monday”
    • Known fact T: “I was out of town on Monday”
    • Conclusion: Since C contradicts T, statement S must be false
  • Mathematical formulation
    • If \(S\) implies \(C\), but \(C\) is false, then \(S\) is false
    • To prove something is true, assume its opposite and seek contradiction
    • Essential technique for impossibility proofs in computer science
    • Note this proof technique does not constructively show what is true

Programs analyzing other programs

  • A program can analyze itself or another program
  • Completed at the level of source code text or output

Self-reflection in computation

  • Programs can analyze themselves:
    • countLines(rf('countLines.py')) reports its line count
    • Similar to how human brains can think about thinking
    • Key: programs can examine their own structure
  • Self-reflection extends and limits computation:
    • Self-reflection enables powerful computation
    • But it also creates fundamental limitations
    • Capability that makes programs powerful also constrains them
    • Key: Some problems become impossible due to self-reflection

Foundation of impossibility

  • Programs can analyze themselves
  • Programs can analyze other programs
  • Self-reflection creates contradictions
  • Proof by contradiction reveals impossibility
  • Discover fundamental limits of computation

Decision programs we need

Program analysis examples

# define analysis functions
def containsGAGA(inString: str) -> str:
    return 'yes' if 'GAGA' in inString else 'no'

def yes(inString: str) -> str:
    return 'yes'

def longerThan1K(inString: str) -> str:
    return 'yes' if len(inString) > 1000 else 'no'

# simulate program files
programs = {
    'containsGAGA.py': 'def containsGAGA(inString):\n    return "yes" if "GAGA" in inString else "no"',
    'yes.py': 'def yes(inString):\n    return "yes"',
    'geneticString.txt': 'CTGAGAATTCGAGA'
}

def rf(filename: str) -> str:
    return programs.get(filename, 'File not found')

# programs analyzing other programs
examples = [
    ("containsGAGA('CTGAGAT')", containsGAGA('CTGAGAT')),
    ("containsGAGA(rf('geneticString.txt'))", containsGAGA(rf('geneticString.txt'))),
    ("yes(rf('containsGAGA.py'))", yes(rf('containsGAGA.py')))
]

for command, result in examples:
    print(f"{command} = {result}")
containsGAGA('CTGAGAT') = yes
containsGAGA(rf('geneticString.txt')) = yes
yes(rf('containsGAGA.py')) = yes

Review for current programs

Summary of simple decision programs

  • containsGAGA(inString): returns “yes” if input contains “GAGA”, else “no”
  • yes(inString): always returns “yes” regardless of input or input characteristics
  • longerThan1K(inString): returns “yes” if input length > 1000, else “no”
  • Are these programs computable? Yes!
  • Are these programs decidable? Yes!
  • Are these programs tractable? Yes!
  • But, wait, what about yesOnString? Woah, hold on!
  • Or, what about crashOnString? Woah, hold on!

Hypothetical yesOnString

  • Definition: yesOnString(P, I) returns:
    • “yes” if P is a valid Python program, P(I) is defined, and P(I)=“yes”
    • “no” otherwise (invalid program, undefined output, or non-“yes”)
  • Question it answers: Does program P return “yes” on input string I?
  • Important note: We don’t provide the source code for yesOnString
  • Reason: We will prove it’s impossible to write such a program!

The yesOnString program would solve a fundamental question about program behavior. Yet, as we’ll see, asking this question leads to logical contradictions that make the program impossible!

Approximating yesOnString

def yesOnStringApprox(progString, inString):   
    if progString == rf('containsGAGA.py'):
        return containsGAGA(inString)
    elif progString == rf('longerThan1K.py'):
        return longerThan1K(inString)
    elif progString == rf('yes.py'):
        return yes(inString)
    elif progString == rf('maybeLoop.py'):
        if not 'secret sauce' in inString: 
            return 'no'
        else:
            return maybeLoop(inString)
    else:
        return 'unknown' 

def testyesOnStringApprox():
    testvals = [
        ('containsGAGA.py', 'TTTTGAGATT', 'yes'),
        ('containsGAGA.py', 'TTTTGAGTT', 'no'),
        ('longerThan1K.py', 1500*'x', 'yes'),
        ('longerThan1K.py', 'xyz', 'no'),
        ('yes.py', 'xyz', 'yes'),
        ('maybeLoop.py', '', 'no'),
        ('maybeLoop.py', 'asdfhjksd', 'no'),
        ('maybeLoop.py', 'secret sauce', 'yes'),
        ('maybeLoop.py', 'xsecret sauce', 'no'),
        ('maybeLoop.py', 'xsecret saucex', 'yes'),
    ]
    for (filename, inString, solution) in testvals:
        val = yesOnStringApprox(rf(filename), inString)
        utils.tprint(filename + ":", val)
        assert val == solution
  • Approximates yesOnString for known programs and inputs

The yesOnSelf simplification

  • Definition: yesOnSelf(P) returns:
    • "yes" if P is a valid Python program and P(P) = “yes”
    • "no" otherwise
  • Relationship to yesOnString:
    • yesOnSelf(P) = yesOnString(P, P)
    • Asks: “Does program P return ‘yes’ when given itself as input?”
  • Why this matters for proofgrammers:
    • Simpler to analyze than the two-parameter version
    • Self-application creates the conditions for contradiction

Examples with yesOnSelf

  • yesOnSelf(“not a program”) returns “no”
  • yesOnSelf(“letters and numbers 1431”) returns “no”
  • yesOnSelf(“containsGAGA.py”) returns “yes”
  • yesOnSelf(“longerThan1K.py”) returns “no”
  • yesOnSelf(“yesOnSelf.py”) returns “yes” or “no” or \(...\)?

Get Ready: What is the behavior of the yesOnSelf program when it is run on itself? We are going to introduce a “troublemaker” program that will create a contradiction and shed light on this issue!

Using hypothetical yesOnSelf

# hypothetical behavior of yesOnSelf
def hypothetical_yesOnSelf(P: str) -> str:
    """HYPOTHETICAL: Check if program P returns 'yes' on itself."""
    if P == "yes.py":
        return "yes"
    elif P == "containsGAGA.py":
        return "yes"
    else:
        return "no"

# show examples
test_programs = ["yes.py", "containsGAGA.py", "longerThan1K.py"]

print("Hypothetical yesOnSelf behavior:")
for prog in test_programs:
    result = hypothetical_yesOnSelf(prog)
    print(f"yesOnSelf('{prog}') = {result}")
Hypothetical yesOnSelf behavior:
yesOnSelf('yes.py') = yes
yesOnSelf('containsGAGA.py') = yes
yesOnSelf('longerThan1K.py') = no

Devastating notYesOnSelf

# here's where the contradiction emerges!
def notYesOnSelf(P: str) -> str:
    """Return the opposite of what yesOnSelf would return."""
    result = hypothetical_yesOnSelf(P)
    return "no" if result == "yes" else "yes"

# test on some programs
test_cases = ["yes.py", "containsGAGA.py"]
for prog in test_cases:
    yes_result = hypothetical_yesOnSelf(prog)
    not_result = notYesOnSelf(prog) 
    print(f"yesOnSelf('{prog}') = {yes_result}")
    print(f"notYesOnSelf('{prog}') = {not_result}")
yesOnSelf('yes.py') = yes
notYesOnSelf('yes.py') = no
yesOnSelf('containsGAGA.py') = yes
notYesOnSelf('containsGAGA.py') = no

Running notYesOnSelf

  • notYesOnSelf(“not a program”) returns “yes”
  • notYesOnSelf(“letters and numbers 1431”) returns “yes”
  • notYesOnSelf(“yes.py”) returns “no”
  • notYesOnSelf(“containsGAGA.py”) returns “no”
  • notYesOnSelf(“longerThan1K.py”) returns “yes”
  • notYesOnSelf(“notYesOnSelf.py”) returns \(...\) what?!

Amazing: Since no output is correct when notYesOnSelf is run on itself, we have discovered that yesOnSelf cannot exist! Let’s explore these scenarios in greater detail to build understanding.

Revealing the contradiction

  • Consider: What happens when we run notYesOnSelf("notYesOnSelf.py")?

  • Case 1: Suppose it returns "yes"

    • If notYesOnSelf returns “yes” on itself, then by definition, notYesOnSelf should return “no” (opposite of what it actually returned). Contradiction!
  • Case 2: Suppose it returns "no"

    • If notYesOnSelf returns “no” on itself, then by definition, notYesOnSelf should return “yes” (opposite of what it actually returned). Contradiction!

No output is correct! Whether notYesOnSelf returns “yes” or “no” on itself, it contradicts its own definition. Therefore, notYesOnSelf cannot exist and is a logical impossibility!

The impossibility proof structure

  • Step 1: Proof by contradiction
    • Assume yesOnString exists (our statement S)
    • This would allow us to create yesOnSelf
    • And then create notYesOnSelf
  • Step 2: Derive contradiction
    • notYesOnSelf("notYesOnSelf.py") creates logical contradiction
    • Returns “yes” if and only if it returns “no”
    • No program can have this impossible property
  • Step 3: Conclude impossibility
    • Since assuming yesOnString exists leads to contradiction
    • This means that yesOnString cannot logically exist

Practical implications

  • No perfect bug finder
    • Cannot create a program that finds all bugs in all programs
    • Static analysis tools work on many cases but not universally
    • Software companies invest heavily in partial solutions
  • No universal crash detector
    • Cannot predict crashes for all possible program inputs
    • Testing and runtime monitoring remain essential
    • Impossibility doesn’t mean “useless” — it means “not universal”!
  • Fundamental computational limits
    • Some problems are unsolvable even with infinite resources
    • Proofgrammers must understand these boundaries

Theory versus practice

  • What is impossible
    • Universal bug detection for all programs
    • Perfect crash prediction for all inputs
    • Determining if any arbitrary program halts
  • What is possible
    • Bug detection for many specific program classes
    • Crash prediction for many common scenarios
    • Static analysis tools that work on practical codebases
  • Proofgrammer perspective
    • Impossibility results guide what tools to build
    • Focus effort on solvable sub-problems
    • Understand fundamental computational boundaries

Why impossibility matters

  • Scientific understanding:
    • Reveals fundamental limits of computation itself
    • Not about hardware limitations or current technology
    • Shows logical boundaries that no algorithm can cross
  • Practical guidance:
    • Helps avoid impossible engineering goals
    • Guides research toward productive directions
    • Informs realistic expectations for automated tools

Understanding impossibility is as important as understanding possibility. It prevents wasted effort on unsolvable problems and focuses innovation on achievable goals.

Key takeaways

  • Proof by contradiction: Natural technique for impossibility results
  • Self-reflection: Programs analyzing themselves creates contradictions
  • Universal impossibilities: yesOnString and crashOnString cannot exist
  • Practical implications: Static analysis works on sub-problems, not universally
  • Wow, this was really brain breaking! Now, you should:
    • Read the chapter and hand-write or type out the key results
    • Explain the key results to a member of this class
    • Explain the key results to the instructor in office hours
    • Bring your questions to the proofgrammer charette session