Slide 1

Slide 1 text

IMPOSSIBLE PROGRAMS @tomstuart / Scottish Ruby Conference / 2013-05-12

Slide 2

Slide 2 text

WE ARE COMPUTER PROGRAMMERS

Slide 3

Slide 3 text

WRITE TONS OF COMPUTER PROGRAMS WE

Slide 4

Slide 4 text

IT’S OUR JOB

Slide 5

Slide 5 text

PROGRAMS ARE PRETTY GOOD I SUPPOSE

Slide 6

Slide 6 text

CAN’T DO PROGRAMS EVERYTHING

Slide 7

Slide 7 text

WHY NOT?

Slide 8

Slide 8 text

VAGUE DIFFICULT EXPENSIVE IMPOSSIBLE

Slide 9

Slide 9 text

ho can a PROGRAM be IMPOSSIBLE?

Slide 10

Slide 10 text

WE DEMAND UNIVERSAL SYSTEMS

Slide 11

Slide 11 text

We can translate any Python program into Ruby. We can translate any Ruby program into Python. We can implement a Python interpreter in Ruby. We can implement a Ruby interpreter in Python. We can implement a JavaScript interpreter in Ruby. We can implement a Ruby interpreter in JavaScript. We can implement a Turing machine simulator in Ruby. We can implement a Ruby interpreter as a Turing machine.

Slide 12

Slide 12 text

JavaScript Ruby Python Lambda calculus Turing machines SKI calculus Tag systems Partial recursive functions Game of Life Rule 110 C++ Haskell Lisp Register machines Magic: The Gathering C Java XSLT

Slide 13

Slide 13 text

Universal systems can run software. We don’t just want machines, we want general-purpose machines.

Slide 14

Slide 14 text

PROGRAMS ARE DATA

Slide 15

Slide 15 text

>> puts 'hello world' hello world => nil >> program = "puts 'hello world'" => "puts 'hello world'" >> bytes_in_binary = program.bytes.map { |byte| byte.to_s(2).rjust(8, '0') } => ["01110000", "01110101", "01110100", "01110011", "00100000", "00100111", "01101000", "01100101", "01101100", "01101100", "01101111", "00100000", "01110111", "01101111", "01110010", "01101100", "01100100", "00100111"] >> number = bytes_in_binary.join.to_i(2) => 9796543849500706521102980495717740021834791

Slide 16

Slide 16 text

>> number = 9796543849500706521102980495717740021834791 => 9796543849500706521102980495717740021834791 >> bytes_in_binary = number.to_s(2).scan(/.+?(?=.{8}*\z)/) => [ "1110000", "01110101", "01110100", "01110011", "00100000", "00100111", "01101000", "01100101", "01101100", "01101100", "01101111", "00100000", "01110111", "01101111", "01110010", "01101100", "01100100", "00100111"] >> program = bytes_in_binary.map { |string| string.to_i(2).chr }.join => "puts 'hello world'" >> eval program hello world => nil

Slide 17

Slide 17 text

UNIVERSAL SYSTEMS + PROGRAMS ARE DATA = INFINITE LOOPS

Slide 18

Slide 18 text

Every universal system can simulate every other universal system, including itself. More specifically: every universal programming language can implement its own interpreter.

Slide 19

Slide 19 text

def evaluate(program, input) # parse program # evaluate program on input while capturing output # return output end

Slide 20

Slide 20 text

>> evaluate('print $stdin.read.reverse', 'hello world') => "dlrow olleh"

Slide 21

Slide 21 text

def evaluate(program, input) # parse program # evaluate program on input while capturing output # return output end def evaluate_on_itself(program) evaluate(program, program) end

Slide 22

Slide 22 text

>> evaluate_on_itself('print $stdin.read.reverse') => "esrever.daer.nidts$ tnirp"

Slide 23

Slide 23 text

def evaluate(program, input) # parse program # evaluate program on input while capturing output # return output end def evaluate_on_itself(program) evaluate(program, program) end program = $stdin.read if evaluate_on_itself(program) == 'no' print 'yes' else print 'no' end does_it_say_no.rb

Slide 24

Slide 24 text

$ echo 'print $stdin.read.reverse' | ruby does_it_say_no.rb no $ echo 'print "no" if $stdin.read.include?("no")' | ruby does_it_say_no.rb yes $ ruby does_it_say_no.rb < does_it_say_no.rb ???

Slide 25

Slide 25 text

does_it_say_no.rb yes no never finish other output? does_it_say_no.rb ✘ ✔ ✘ ✘

Slide 26

Slide 26 text

Ruby is universal so we can write #evaluate in it so we can construct a special program that loops forever

Slide 27

Slide 27 text

s here' one PROGRAM IMPOSSIBLE

Slide 28

Slide 28 text

Sometimes infinite loops are bad. We could remove features from Ruby until there’s no way to cause an infinite loop.

Slide 29

Slide 29 text

remove while / until / loop, only allow iteration over finite data structures to prevent -> x { x[x] }[-> x { x[x] }] only allow a method to call other methods whose names come later in the alphabet • No unlimited iteration • No procs • No recursive method calls • No blocking I/O • ...

Slide 30

Slide 30 text

Call this language “Rub”. It must be impossible to write a Rub interpreter in Rub.

Slide 31

Slide 31 text

if we could write #evaluate in Rub so it must be impossible to write #evaluate in Rub then we could use it to construct a special program that loops forever but Rub doesn’t let you write programs that loop forever

Slide 32

Slide 32 text

(That’s weird, because a Rub interpreter always finishes eventually, so it feels like we should be able to write it in Rub.)

Slide 33

Slide 33 text

We could write a Rub interpreter in some other more powerful language that also forbids infinite loops... ...but that language can’t implement its own interpreter either.

Slide 34

Slide 34 text

ABOUT RUBY? WHAT okay but

Slide 35

Slide 35 text

#evaluate is an impossible program for Rub, which means that Rub isn’t universal. Universal systems like Ruby have impossible programs too.

Slide 36

Slide 36 text

input = $stdin.read puts input.upcase This Ruby program always finishes.* * assuming STDIN is finite & nonblocking

Slide 37

Slide 37 text

input = $stdin.read while true # do nothing end puts input.upcase This Ruby program always loops forever.

Slide 38

Slide 38 text

Can we write a Ruby program that can decide this in general?

Slide 39

Slide 39 text

input = $stdin.read output = '' n = input.length until n.zero? output = output + '*' n = n - 1 end puts output

Slide 40

Slide 40 text

input = $stdin.read output = '' n = input.length until n.zero? output = output + '*' n = n - 2 end puts output

Slide 41

Slide 41 text

require 'prime' def primes_less_than(n) Prime.each(n - 1).entries end def sum_of_two_primes?(n) primes = primes_less_than(n) primes.any? { |a| primes.any? { |b| a + b == n } } end n = 4 while sum_of_two_primes?(n) n = n + 2 end print n

Slide 42

Slide 42 text

def halts?(program, input) # parse program # analyze program # return true if program halts on input, false if not end

Slide 43

Slide 43 text

>> halts?('print $stdin.read', 'hello world') => true >> halts?('while true do end', 'hello world') => false

Slide 44

Slide 44 text

def halts?(program, input) # parse program # analyze program # return true if program halts on input, false if not end def halts_on_itself?(program) halts?(program, program) end program = $stdin.read if halts_on_itself?(program) while true # do nothing end end do_the_opposite.rb

Slide 45

Slide 45 text

$ ruby do_the_opposite.rb < do_the_opposite.rb

Slide 46

Slide 46 text

do_the_opposite.rb eventually finish loop forever do_the_opposite.rb ✘ ✘

Slide 47

Slide 47 text

Every real Ruby program must either loop forever or not, but whichever happens, #halts? will be wrong about it. do_the_opposite.rb forces #halts? to give the wrong answer.

Slide 48

Slide 48 text

if we could write #halts? in Ruby so it must be impossible to write #halts? in Ruby then we could use it to construct a special program that forces #halts? to give the wrong answer but a correct implementation of #halts? would always give the right answer

Slide 49

Slide 49 text

This is the halting problem.

Slide 50

Slide 50 text

WHO CARES? okay but

Slide 51

Slide 51 text

We never actually want to ask a computer whether a program will loop forever. But we often want to ask computers other questions about programs.

Slide 52

Slide 52 text

def prints_hello_world?(program, input) # parse program # analyze program # return true if program prints "hello world", false if not end

Slide 53

Slide 53 text

>> prints_hello_world?('print $stdin.read.reverse', 'dlrow olleh') => true >> prints_hello_world?('print $stdin.read.upcase', 'dlrow olleh') => false

Slide 54

Slide 54 text

def prints_hello_world?(program, input) # parse program # analyze program # return true if program prints "hello world", false if not end def halts?(program, input) hello_world_program = %Q{ program = #{program.inspect} input = $stdin.read evaluate(program, input) # evaluate program, ignoring its output print 'hello world' } prints_hello_world?(hello_world_program, input) end

Slide 55

Slide 55 text

if we could write #prints_hello_world? in Ruby so it must be impossible to write #prints_hello_world? in Ruby then we could use it to construct a correct implementation of #halts? but it’s impossible to correctly implement #halts? in Ruby

Slide 56

Slide 56 text

Not only can we not ask “does this program halt?”, we also can’t ask “does this program do what I want it to do?”.

Slide 57

Slide 57 text

This is Rice’s theorem: Any interesting property of program behaviour is undecidable.

Slide 58

Slide 58 text

WHY HAPPEN? DOES THIS

Slide 59

Slide 59 text

We can’t look into the future and predict what a program will do. The only way to find out for sure is to run it. But when we run a program, we don’t know how long we have to wait for it to finish. (Some programs never will.)

Slide 60

Slide 60 text

Any system with enough power to be self-referential can’t correctly answer every question about itself. We need to step outside the self-referential system and use a different, more powerful system to answer questions about it. But there is no more powerful system to upgrade to.

Slide 61

Slide 61 text

HOW COPE? CAN WE

Slide 62

Slide 62 text

• Ask undecidable questions, but give up if an answer can’t be found in a reasonable time. • Ask several small questions whose answers provide evidence for the answer to a larger question. • Ask decidable questions by being conservative. • Approximate a program by converting it into something simpler, then ask questions about the approximation.

Slide 63

Slide 63 text

Stuart Understanding Computation From Simple Machines to Impossible Programs Tom Stuart Understanding Computation THE END SCOTCOMP (50% off ebook, 40% off print http://computationbook.com/ @tomstuart / [email protected]