Reasoning About Assignment Statements
A program is correct when it produces the correct result as specified, so correctness is always measured against a specification.
Try it: Assignment, Statement by Statement
How an assignment statement evaluates its right side first (using the names' current values) and only then binds the name on the left: updating x = x + 1, augmented operators like += and //=, chained a = b = 0, simultaneous a, b = b, a + b versus one-at-a-time assignment, rebinding a name to a value of another type, the NameError for a name that was never assigned, and the SyntaxError for a name that breaks the naming rules.
How it works
- Before anything runs, Python compiles the whole program; a name that breaks the rules (starts with a digit, contains a space or a dash, or is a keyword) is a SyntaxError and no line runs at all.
- For each assignment, evaluate the complete right side first, looking every name up in the variable table as it is now.
- Then bind the name(s) on the left to that value: a new name is added to the table, an existing one is replaced (even by a value of a different type).
- x += 5 reads x, applies the operator (+, -, *, //, %, **) and binds the result back to x; a = b = 0 evaluates 0 once and binds a, then b.
- a, b = b, a + b builds the whole right-hand tuple from the OLD values before binding a and b; two separate statements would use the new a in the second line.
- Reading a name that is not in the table raises NameError and the program stops at that line.
Default run (10 steps): A 4-line program. Python first compiles all of it, then runs it top to bottom. The variable table on the right starts empty. … Program finished. Final table: x = 16.
Simplified: The programs are fixed templates: you choose the template, its numbers, the operator and the variable name, never the code. The lab's own small Python tokenizer and parser (written for the statements these programs use: assignments, print calls, if / elif / else, for, while, def and return) checks the text; nothing is executed as real Python. Every program the lab can show was compiled by CPython 3.12 and the lab reproduces its exception type, message, line, caret columns and the printed report. A small interpreter written for the same statements then runs them (integers of any size, the str/float results these templates produce, and print); each program was also run in CPython 3.12 under sys.settrace, and the lab reproduces the variables after every line, the output and the error.
Loading the simulation…
Correct Relative to What
Most programmers say a program is correct when it works. That phrase hides a question: works relative to what? A program can run without crashing, print sensible-looking output, and still fail the task it was written for. To talk precisely about correctness, we need a yardstick that exists outside the program itself. That yardstick is the specification.
The source states the central idea directly: a program is correct when it produces the correct result as specified, so correctness is always measured against a specification. Keep that sentence in mind. Every claim in this article about proofs, states, and statements is in service of checking a program against its specification.
What a Specification Gives You
A specification describes what should hold before the program runs and what must hold after it finishes. The program is correct when, for the inputs it is meant to handle, its final result satisfies the specification.
Notice that the specification sits outside the program. You can write a program that is perfectly faithful to its own steps and still be wrong, if the specification it was checked against describes the wrong goal. The source puts it plainly: a proof of correctness is only as trustworthy as the specification it relies on.
In the diagram, the specification is the standard the check uses, and the actual final state is what the run really produced. A correctness check compares the two. If the observed final state satisfies what the specification demands, the run agrees with the specification. The diagram is an illustration built for this article; the source supplies the idea that correctness is measured against a specification.
State and Process During Execution
The state of a program is all information relevant at one moment. A process is the sequence of states the computer passes through during execution.
Think of state as a snapshot. At any instant, the program has a set of variables, each holding some value. That collection is the state. As statements run, the snapshot changes. The full run of the program is the chain of these snapshots, one after another. That chain is the process.
One way to picture a single snapshot is as a mapping from variable names to their current values. The diagram below shows a generated example with three variables after three statements have run. It is a single moment in the process, not the whole process.
In this snapshot, z is the variable whose value was most recently set, so it is marked as changed. The state at this moment contains all three variables and their values. The state immediately before the last statement ran would have contained only x and y, with z not yet holding a value.
How an Assignment Changes the State
The meaning of a statement can be expressed as its effect on the computer's state. This is the key move that makes proofs possible. Instead of asking what a statement means in the abstract, we ask what it does to the snapshot. For an assignment such as x := x + 1, the effect is that the variable x ends up holding the old value of x plus one, while the other variables are unchanged.
What do you think happens?
Before the statement runs, x holds 5. The statement x := x + 1 executes. What does x hold now?
Reveal answer
x holds 6.
The right-hand side is evaluated using the current value of x, which is 5, giving 6. That value is then stored in x. Only x changes; the other variables in the state keep their values.
Tracing a sequence of states
Start with a state where x = 5 and y = 2. Run the statements x := x + 1 and then y := y + x. What is the state after each statement?
- 1Initial state
The state holds x = 5 and y = 2. This is the first snapshot in the process.
- 2After x := x + 1
The right-hand side uses the current x, which is 5, so x becomes 6. The state is now x = 6 and y = 2. Only x changed.
- 3After y := y + x
The right-hand side uses the current y and x, which are 2 and 6, so y becomes 8. The state is now x = 6 and y = 8. Only y changed in this step.
- 4Checking the process
The process is the sequence of three states: x = 5 and y = 2, then x = 6 and y = 2, then x = 6 and y = 8. Each statement's effect is a fact that a proof can rely on.
Final state: x = 6 and y = 8. The process passed through three states, and each statement changed the state in a predictable way.
From Single Steps to a Proof
The reasoning behind correctness proofs is a single chain. The specification says what the correct result must be. The program's execution is a process made of successive states. Each statement's effect on state is a fact you can rely on. A proof strings those facts together to show that the final state of every run matches what the specification demands. Removing any link weakens the whole argument.
A program is provably correct when it can be demonstrated mathematically that the sequence of computations it describes will always produce the correct result. The important word is always. The proof reasons about every possible run of the program, not just the few runs someone happened to try. This is what makes a proof stronger than testing alone. A test shows behavior on the inputs you chose; a proof addresses all inputs the specification covers.
Why Proofs Are Hard and Why Correct Can Mean Useless
Rigorous proof is genuinely hard. In practice, full mathematical proofs of correctness are typically limited to fairly small programs. This does not make the ideas useless for larger work. Even in everyday programming, the techniques used in correctness proofs can guide how you reason about code, even when you never write a formal proof.
A second question is harder to see. Many beginners assume that once a program has been shown to be correct, the job is finished and the program can be trusted to do what its users need. This quietly mixes two questions. The first is whether the program does what it was asked to do. The second is whether what it was asked to do was the right thing in the first place. A correct program can still be useless if its specification is wrong.
Mistakes Learners Make About Correctness
Common mistake Treating correct as meaning useful
What it looks likeA program passes a proof that it computes the sum of the inputs, but the user actually needed the average.
Why it failsCorrectness is measured against the specification. If the specification asks for the wrong thing, the program can match it exactly and still fail the user.
Do this insteadCheck two questions separately: does the program meet its specification, and is the specification the right one for the real problem.
Common mistake Assuming a test run is the same as a proof
What it looks likeA program is tried on three inputs and works each time, so the writer concludes it is proven correct.
Why it failsA proof reasons about every possible run. Three runs say nothing about the runs that were never tried.
Do this insteadDescribe testing as evidence on chosen cases, and reserve proof language for arguments that cover every run.
Common mistake Thinking a statement's meaning is its appearance
What it looks likeReading x := x + 1 as a mathematical equation that is always false because x cannot equal x plus one.
Why it failsThe meaning of an assignment is its effect on state: the new value of x is the old value plus one.
Do this insteadRead the statement as a change to the current snapshot, using the value x holds just before the statement runs.
Common mistake Forgetting that the state includes every variable, not only the one being assigned
Practice Tracing State
Start with a state where a = 3 and b = 4. Run the statements a := a + b, then b := b + 1. Write the state after each statement. Then explain whether a specification saying that a ends up equal to 7 would be satisfied by this run.
Hints
- Update the state one statement at a time. The right-hand side of each statement uses the values in the current snapshot.
- After a := a + b, a should be 7 and b should still be 4. Then check what b becomes.
- Compare the final value of a with the specification, and remember the check is about the final state.
Putting the Ideas Together
- 1A program is correct when it produces the correct result as specified, so correctness is always measured against a specification.
- 2The state is all information relevant at one moment, and the process is the sequence of states passed through during execution.
- 3The meaning of a statement is its effect on state, and a proof strings those effects together to show that every run ends in a state the specification allows.
- 4Proofs are hard and usually limited to small programs, and a correct program can still be useless if its specification describes the wrong goal.
Key Takeaways
- A program is correct when it produces the correct result as specified, so correctness is always measured against a specification.
- The state is all information relevant at one moment, and the process is the sequence of states passed through during execution.
- The meaning of a statement is its effect on state, and a proof strings those effects together to show that every run ends in a state the specification allows.
- Proofs are hard and usually limited to small programs, and a correct program can still be useless if its specification describes the wrong goal.