First-class continuation 2026-10-31
A continuation represented as an ordinary language value that can be stored and later invoked. Whether it may be invoked once or repeatedly, and whether mutable program state is restored, are separate semantic choices.
Frame condition Created 2026-10-25 Updated 2026-10-31
A clause stating which parts of a program state an operation preserves. A query's returned value alone does not prohibit it from also mutating the receiver; a binary update's result equation alone does not preserve its distinct operand. Explicit frame conditions rule out these behaviors and make substitution and composition arguments possible.
Partial correctness Created 2026-10-18 Updated 2026-10-24
Partial correctness means that every terminating execution begun in a program state satisfying the precondition finishes in a program state satisfying the postcondition . For a sequential program , the usual Hoare triple notation isHere the notation expresses partial correctness; it makes no promise that finishes. The precondition and postcondition describe permitted starting circumstances and required final circumstances, rather than tests performed automatically by the program.[ref]
An Axiomatic Basis for Computer Programming by Tony Hoare. Hoare, 1969, §3 Program Execution, p. 577, and §4 General Reservations, pp. 578–579; the paper instead labels the precondition P, program Q and result R
For a sharp example, take the precondition and the following sequential program over mathematical integers:The condition remains true, so execution never reaches the instruction after the loop. Consequently its partial correctness claim holds for the postcondition , and even for the impossible postcondition : there is no terminating execution with a wrong final result. This is vacuous truth, not evidence that the loop computes a sum. The same program fails total correctness for this permitted input.
i = 0
s = 0
while i < n:
i = iThe distinction is useful in a proof: first show that an answer, whenever produced, is right; then establish algorithm termination. The summation loop verification supplies both arguments for a loop that actually progresses. Its finite trace illustrates one execution, while its loop invariant covers every permitted input.
Thread the explicit program state through the computation, using the base computational monad to sequence its effects:In Haskell notation, with
resultM and bindM standing for the base operations:result :: Monad m => a -> MemT m a
result x s = resultM (x,s)
bind :: Monad m => MemT m a -> (a -> MemT m b) -> MemT m b
bind p f s = bindM (p s) (\(x,s') -> f x s')This is the state monad transformer. The first computation's final program state, rather than its initial program state, becomes the second computation's input. The base computational monad decides whether that pair is available at all.
Use the three monad laws of the base computational monad. For left identity, at an arbitrary program state ,The last step is the base left identity law. For right identity,The penultimate equality identifies the pair-destructuring function with ; the last uses the base right identity law.
For associativity, expand the two successive uses of sequencing and apply base associativity:No program state is duplicated or reset during these rearrangements: both sides pass to , its output to , and the output of to . By function extensionality,
type IdM a = a
result :: a -> IdM a
result x = x
bind :: IdM a -> (a -> IdM b) -> IdM b
bind x f = f xAll three monad laws reduce to substitution in function application. There is no additional program state, failure alternative or continuation layer:
Expand the two type synonyms to see what survives an exception:In the first order, a failed computation can still produce
(Nothing,s'). The exception handler therefore runs with , including writes made before failure. In the second order, failure produces Nothing, with no returned computer memory. A handler defined by applying the underlying orelse pointwise runs its alternative from the original input computer memory . This gives rollback of this explicit program state for the failed branch.For example, start with a memory location holding , write , fail, and handle the failure by reading that memory location. The first order returns ; the second returns . This rollback concerns the explicit computer memory only: arbitrary effects in the base computational monad are not thereby reversed.A conventional programming language normally retains writes made before an exception is caught. Its mutable computer memory and exception handling therefore correspond more closely to .
A loop invariant is a property of the program state that holds whenever control reaches the loop's condition, including the final failed test. Its main purpose is proving partial correctness: establish it initially, prove one guarded iteration preserves it, then combine it with the negated condition to obtain the postcondition. A separate loop variant or another progress argument proves algorithm termination and hence total correctness.
To preserve the actual printed function, including its handling of nonpositive exponents and wrapping
Int bases, use a tail-recursive accumulator with the same multiplication order:def anotherfuncRec(x: Int, n: Long): Double = {
require(x > 0)
@scala.annotation.tailrec
def loop(y: Int, i: Long, result: Double): Double = {
if (i <= 0) result
else {
val nextResult = if (i % 2 == 1) result * y else result
loop(y * y, i / 2, nextResult)
}
}
loop(x, n, 1.0)
}Each recursive call corresponds to one source iteration and carries exactly its next program state. Hence it reproduces the printed numerical behavior, rather than silently repairing it. For positive
n, both routines use steps; for nonpositive n, both take constant time. The recursive call is in tail position, so Scala's tail-call optimization avoids a growing call stack; the annotation asks the compiler to check that condition.If the intended exact power is required instead, change the base and accumulator parameters of this same recurrence to
BigInt and require n >= 0, as in part (d). Its mathematical loop invariant becomes a recursion invariant, with the same logarithmic number of multiplication steps.Every operation requires . The queries have postconditions “return ” and “return ” respectively, leaving both functions unchanged. An incoming increment additionally requires and hasThe outgoing increment analogously requires , increments only , and preserves . These frame conditions prevent an implementation from accidentally changing another user's count. Here a subscript zero denotes the old program state.
trait Payments {
// All methods require 1 <= user && user <= 99999999.
// Queries leave the abstract state unchanged.
def incoming(user: Int): Int
def outgoing(user: Int): Int
// Require the corresponding old count < Int.MaxValue.
// Increment only that user's named count by one.
def recordIncoming(user: Int): Unit
def recordOutgoing(user: Int): Unit
}The preconditions and postconditions are part of the formal specification; the trait's type signatures alone cannot express all of them. The implementation below uses checked
require calls for the relevant invalid operations.For each table index , let be the set of non-null nodes reachable by following
next. A sufficient representation invariant is:- The array has exactly buckets, and every bucket heads a finite acyclic linked list.
- The sets are pairwise disjoint, so one physical node cannot belong to two buckets.
- Every reachable node has a valid user identity, lies in bucket
hash(node.user), and has both counters between zero andInt.MaxValue. - User identities are unique among all reachable nodes, not merely within a single chain.
The abstraction function maps these nodes to the total functions of part (a): if the unique node for exists, and are its fields; otherwise both are zero. A stored record with two zero counters is harmless and has the same observable meaning as an absent record. This intentionally permits multiple concrete representations of the same abstract program state.
Initially every bucket is null, so the representation invariant holds and the abstraction function yields all zeros. The successful-update branch preserves every structural condition; the insertion branch creates a fresh node with a previously absent identity in its correct bucket. Both branches realize exactly the incoming-increment postcondition, demonstrating correct operations through an abstraction function.
Postcondition 2026-10-18
The condition required of a final program state when a program finishes under its precondition. The summation loop verification requires returned value to satisfy and preserves the input . Relating the answer to the original input matters: allowing the program to replace by zero would change the intended contract.
A postcondition alone gives no promise of algorithm termination. It also need not specify a unique output: allows many answers, while the exact triangular number equation determines one integer answer for each permitted . Stronger postconditions express more obligations; proving only a weaker consequence cannot justify the stronger requirement.
Precondition Created 2026-10-18 Updated 2026-10-25
The condition required of a starting program state for a stated correctness guarantee. In the summation loop verification, and admit and , but exclude . The accompanying model assumes exact integer arithmetic and terminating elementary operations. The initial values of need no restriction because initialization overwrites them before use.
A precondition is a logical assumption, not automatically an executable check. Writing
require in a procedure states its contract; an implementation that checks or rejects invalid inputs needs that behavior specified too. Weakening a precondition admits more starts and therefore demands more proof: dropping here makes a terminating counterexample to , since initialization returns zero while . Program variable 2026-11-05
A program variable is a named binding or storage abstraction used to supply a value during execution of a computer program. Its precise meaning belongs to the programming language semantics: some languages describe mutable storage locations, while others emphasize names associated with values. The current semantic environment and program state determine what an occurrence denotes. A mathematical variable and a program variable consequently need not obey the same rules.
For example, in Python, executing
x = 3 followed by x = x + 1 first evaluates the right-hand program expression using the old binding and then binds x to the resulting integer 4. This variable assignment is a change in the program state, not the equation .[ref]Simple statements, Python 3.14 language reference. §7.2, Assignment statements, evaluation and identifier targets
Binding a second name does not necessarily copy the underlying data. After
a = list() and b = a, executing b.append(7) changes the list also reached through a. By contrast, b = list() rebinds only b. Distinguishing changes to data from changes to names makes shared mutable structures easier to reason about.[ref]Simple statements, Python 3.14 language reference. §7.2, rebinding names versus modifying mutable objects
State monad 2026-10-31
A computation represented by a function from an initial program state to a result and a final program state. Its sequencing passes each output program state to the next computation.
State monad transformer 2026-10-31
A state monad layered over a base computational monad. An unsuccessful base computation may return no final program state; this matters when combining it with an exception monad transformer.
Summation loop verification Created 2026-10-18 Updated 2026-10-31
Compute , the triangular number , for a nonnegative integer . The empty sum gives zero when . Use exact mathematical integers and keep the input unchanged; every comparison and variable assignment is a terminating operation. The original sequential procedure isThe order of the two variable assignments matters: the addition uses the newly incremented . For , the pairs at successive condition checks are . At the last check, is false, and the returned sum is .
require n is an integer and n >= 0
i = 0
s = 0
while i < n:
i = i + 1
s = s + i
return sThe precondition is with , under the exact-arithmetic model just stated. The postcondition for returned value is , with still equal to its starting value. At each condition check use the loop invariantand the loop variant . The equation says that the accumulator is the triangular number for precisely the processed prefix ; the inequalities connect that prefix to the loop's condition.
The summation loop restores its invariant before each condition check
. Original sequential control flow for the summation loop verification. The blue loop head carries the loop invariant; the orange body increments before adding it to . A false condition leads to the postcondition. The loop variant decreases on the return arrow. All values are mathematical integers and remains unchanged. Details.Initialization gives , so holds, including when . For preservation, let the old values satisfy and . Write and for the values after both variable assignments. Since are integers, gives , andThus the completed body restores . Between the two variable assignments, the equation can be false; the loop invariant is required at the loop head. This preservation step is mathematical induction on the number of completed iterations.
At exit, the false condition gives , while gives . Hence , so : this proves partial correctness. For algorithm termination, gives the nonnegative integer . When the condition is true, ; the terminating body gives . Therefore the loop cannot repeat forever. Together these arguments prove total correctness for every permitted input, including .
Two changes expose what the proof checks. Swapping the two body instructions computes , returning rather than for . Leaving unchanged can preserve an initial equation forever without producing any answer. The first change breaks the intended postcondition; the second breaks algorithm termination. A trace can reveal these failures, while the two proof obligations explain them.
The exact-arithmetic assumption has a concrete boundary. In a stipulated unsigned eight-bit model where each addition wraps modulo , fits in the input representation, but its intended triangular number is . The stored accumulator instead finishes at , so the mathematical postcondition fails. This is integer overflow, and the proof above does not apply to that altered model. Restricting the same implementation to prevents overflow in its stored loop values, since ; alternatively use a representation supporting the required exact integer arithmetic. The equations in the proof are mathematical statements, not instructions to evaluate products in eight bits.
Total correctness 2026-10-18
Total correctness requires partial correctness and algorithm termination for every program state allowed by the precondition. For a deterministic sequential program, every permitted start must reach a final program state satisfying the postcondition after finitely many steps. Thus an infinite execution can satisfy partial correctness, but refutes total correctness for a permitted start.
In the summation loop verification, the loop invariant establishes the answer conditional on exit, and the loop variant establishes exit. Both parts matter: replacing the body by a step that never returns would defeat total correctness even if every completed iteration preserved the loop invariant. Likewise, a terminating loop with the wrong sum fails partial correctness and therefore fails total correctness.
These obligations depend on the formal specification. A program that rejects every input might correctly implement a rejection service, but does not meet a contract promising a sum for every nonnegative integer. A proof of total correctness also gives no particular physical response-time guarantee: time complexity and the execution environment require their own assumptions.
Type signature 2026-10-24
The type-level description of a value or operation, including parameter and result types. A type signature can rule out some invalid uses, but ordinary type annotations alone do not express every precondition, postcondition or relationship to an abstract program state.

