Lamport Demands Math Specs as Amazon and Xbox Tap TLA+ to Stop Crashes

Leslie Lamport: Thinking Above the Code — Microsoft Research

Microsoft Research

Software engineers who code without writing specifications deceive themselves about thinking, ensuring that every line they produce starts its life as an unmaintainable patch.

AI-assisted summary of Leslie Lamport: Thinking Above the Code — original source. Check the original for full context.

Source published:

Key ideas

  • Leslie Lamport advocates that software developers adopt the discipline of architects by writing specifications before coding, emphasizing that thinking without writing is merely pretending to think. (2:10)
  • Formal specification methods like TLA+ have delivered substantial industrial impact, including preventing critical outages at Amazon Web Services and uncovering a hardware bug in the Xbox 360 memory architecture that would have caused crashes after four hours. (20:40)
  • The vast majority of software execution can be modeled using standard behavioral state machines composed of an initial state predicate and a next-state relation, without needing complex temporal logic for safety properties. (14:18)
  • Lamport strongly rejects industry practices like 'specification by example' and testing as substitutes for formal specifications, noting that examples obscure edge cases and third-party library reuse routinely fails when behavioral specs are missing. (52:15)

Detailed notes

## Background Published by Microsoft Research on YouTube on July 15, 2014, this video captures Leslie Lamport's plenary address at the Microsoft Faculty Summit. According to the page description, while architects draw detailed blueprints before building, software engineers rarely do; Lamport explains why writing specifications—the software equivalent of blueprints—is vital before coding, especially for complex, concurrent, and distributed systems.

## TL;DR Turing Award laureate Leslie Lamport argues that software development suffers because engineers rush into code without first thinking through their designs on paper. He introduces a continuum of specifications ranging from brief prose to rigorous mathematical formulations like TLA+, demonstrating how state machines and basic mathematical concepts cleanly describe systems without the distorting biases of programming languages. Drawing on concrete examples like non-recursive Quicksort, text pretty-printing, and industry deployments at Amazon Web Services and the Xbox 360, Lamport illustrates that specifying safety properties prevents severe architectural bugs that code testing misses. Ultimately, he contends that writing is the only dependable mechanism for clear thinking, urging developers to treat coding as an implementation detail downstream of architectural design.

## Atmosphere & style The lecture carries an energetic, intellectually demanding, and delightfully irreverent tone, propelled by Lamport's dry wit and no-nonsense delivery. Standing before an audience of computer science academics and researchers, he punctures conventional software engineering dogma with sharp aphorisms, humorously dismissing trendy methodologies while maintaining an infectious passion for mathematical elegance. The experience feels like an uncompromising masterclass from a legendary pioneer who effortlessly demystifies formal methods, alternating between warm humor and unapologetic, candid critiques of lazy programming habits.

## Bulletin - [02:10](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=130s) Leslie Lamport advocates that software developers adopt the discipline of architects by writing specifications before coding, emphasizing that thinking without writing is merely pretending to think. - [20:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1240s) Formal specification methods like TLA+ have delivered substantial industrial impact, including preventing critical outages at Amazon Web Services and uncovering a hardware bug in the Xbox 360 memory architecture that would have caused crashes after four hours. - [14:18](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=858s) The vast majority of software execution can be modeled using standard behavioral state machines composed of an initial state predicate and a next-state relation, without needing complex temporal logic for safety properties. - [52:15](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3135s) Lamport strongly rejects industry practices like 'specification by example' and testing as substitutes for formal specifications, noting that examples obscure edge cases and third-party library reuse routinely fails when behavioral specs are missing.

## Key takeaways - [02:10](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=130s) Thinking must precede coding, and writing is nature's way of revealing sloppy thinking; without writing specifications down, developers are deceiving themselves about system correctness. - [04:10](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=250s) Specifications exist along a wide continuum: short prose for everyday code, mathematical prose for subtle behaviors, and formal languages for mission-critical concurrent and distributed systems. - [06:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=400s) Computer science should mirror the natural sciences by abstracting messy physical execution down to simple mathematical models, primarily mathematical functions and sequences of states. - [08:28](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=508s) Mathematical functions specify input-output transformations but cannot capture execution mechanics, algorithmic differences, or reactive, non-terminating systems like operating systems. - [11:15](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=675s) In the standard behavioral model, execution is represented as a sequence of states, where state transitions are governed by an initial state predicate and a next-state relation operating on unprimed and primed variables. - [13:04](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=784s) System properties split into safety ('nothing bad happens', verifiable at a finite state) and liveness ('something good eventually happens', verifiable only over infinite behaviors), with safety being where the majority of subtle bugs occur. - [17:29](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1049s) Non-determinism does not require esoteric mechanics; it simply means that a single state allows multiple valid assignments of values to the next-state primed variables. - [20:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1240s) Real-world engineering at Amazon and Microsoft demonstrates that formal specifications in TLA+ catch critical concurrency bugs that evade test suites and give teams confidence to make aggressive performance optimizations. - [25:08](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1508s) Informal specifications written in mathematical prose are essential when requirements are subjective or unsuited to automated formal verification, as demonstrated in the design of the TLATeX pretty-printer. - [34:49](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2089s) Thinking at the code level traps programmers in implementation details, such as treating recursion as an intrinsic part of Quicksort rather than modeling it abstractly as non-deterministic transitions over a set of remaining intervals. - [40:51](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2451s) PlusCal allows developers to express algorithms in familiar pseudocode syntax while leveraging arbitrary mathematical expressions and compiling directly to TLA+ for model checking and formal verification. - [40:51](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2451s) Model checkers like TLC are indispensable for concurrent and distributed systems because human intuition alone cannot reliably master complex concurrent interactions. - [48:03](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2883s) Every piece of code written without an upfront specification starts its life as a patch, compounding technical debt and making maintenance progressively more difficult. - [52:15](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3135s) Specification by example and test-driven development are inadequate substitutes for formal specifications because concrete examples restrict attention to happy paths while obscuring rare, critical edge cases. - [55:17](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3317s) Modern code reuse frequently fails in practice because libraries lack rigorous specifications describing what they do, forcing engineers to spend excessive time reverse-engineering internal behaviors. - [57:12](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3432s) TLA+ isolates the complexity of temporal logic almost entirely: roughly 999 lines of a 1,000-line specification are standard discrete mathematics defining safety, with temporal logic reserved solely for the final liveness formula.

## Detailed walkthrough ### [00:01](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1s) Introduction by Judith Bishop

Judith Bishop welcomes attendees back to the plenary session of the Microsoft Faculty Summit to introduce Leslie Lamport, Principal Researcher at Microsoft Research Silicon Valley and recipient of the A.M. Turing Award. Bishop underscores the pervasive impact of Lamport's foundational contributions on modern computing: from students and faculty teaching operating systems courses to anyone interacting with modern computers. His seminal concepts—such as logical clocks, safety and liveness, and sequential consistency—provided the theoretical bedrock that allowed computing systems to transition safely from isolated desktop machines to vast networks of interconnected devices.

Bishop outlines Lamport's storied career across elite institutions and research organizations, including MIT, Brandeis University, Digital Equipment Corporation (DEC), Compaq, and SRI International. Beyond his core computer science research, Lamport has received multiple honorary doctorates and sought to reveal aesthetic elegance to technical practitioners. He created LaTeX to help scientists see the inherent beauty in their written work; now, Bishop notes, he is on a mission to convince software engineers that another discipline holds comparable beauty: mathematics.

[02:10](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=130s) Why, When, and How to Think

Taking the podium, Leslie Lamport begins by framing the central question of his lecture: *“Thinking above the code: why do we think?”* Thinking serves as humanity's primary tool for tackling high-stakes challenges, whether hunting a saber-toothed tiger, constructing a house, or writing a computer program. However, the timing of thought is critical. When hunting a saber-toothed tiger, one must think before stepping outside the cave, because “when the tiger is charging at you, it's a little late.” Similarly, house construction demands that the design be settled before ground is broken; one should never begin figuring out the structure while carpenters are actively nailing down the roof. Applying this principle directly to software development, Lamport argues that developers must think thoroughly before writing a single line of code.

Lamport highlights the disciplined mechanism needed to make thinking effective, referencing cartoonist Richard Guindon: “Writing is nature's way of letting you know how sloppy your thinking is.” Lamport warns against internal mental simulations, asserting that anyone attempting to reason through complex systems without writing is almost certainly deceiving themselves:

> “If you're thinking without writing, chances are you're fooling yourself, we're only pretending to think.”

- **The saber-toothed tiger dilemma:** Prehistoric hunters could not write because writing had not yet been invented, rendering the activity exceptionally dangerous. - **The builder's prerequisite:** Builders draft visual designs prior to physical construction; these architectural drawings are called blueprints. - **The programmer's equivalent:** Software blueprints exist under a different name: *specifications*.

[04:10](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=250s) The Spectrum of Specifications

The moment the word "specification" is uttered, Lamport observes, software engineers reflexively panic at the prospect of indecipherable formal notation, complex mathematical symbols, and rigid academic overhead. But just as construction blueprints span an entire spectrum—from quick, hand-drawn weekend project sketches, to standard municipal house plans, to massive multi-sheet architectural blueprints for skyscrapers—software blueprints exist across a wide continuum. Lamport emphasizes that specifications do not follow a one-size-fits-all methodology, categorizing them into three primary tiers:

1. **Short Prose Specifications:** Suitable for the vast majority of ordinary code. A few straightforward sentences suffice to document intent and structure. 2. **Mathematical Prose:** Required when code exhibits subtle or tricky behavior. It relies on standard natural language prose, but with rigorous mathematical precision. 3. **Formal Specifications:** Essential for systems that are deeply complex, subtle, or mission-critical, particularly concurrent and distributed systems.

When dealing with mission-critical concurrency, engineers must employ software tools to verify that their architectural designs are sound. Lamport underscores that this verification step strictly dictates the notation used: “If you're going to use tools, it means you need a formal language, because tools don't understand prose.”

[06:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=400s) Modeling Reality: From Astronomy to Computer Science

Writing a specification requires clear thinking, and Lamport argues that software engineers should adopt the cognitive framework of computer scientists—that is, the scientific method itself. The defining achievement of science is constructing simple mathematical models to describe complex physical realities. Astronomy serves as the premier historical example: while an actual planet possesses turbulent weather systems, shifting oceans, mountains, and gravitational tides, celestial mechanics made immense strides by abstracting all of those physical intricacies away, modeling each planet simply as a point mass characterized solely by its position and momentum.

```mermaid flowchart TD accTitle: Scientific Modeling in Astronomy and Computing accDescr: Comparison showing how physical reality is abstracted into mathematical models in astronomy and computing.

subgraph Astronomy["Astronomy"] A1["Physical Planet<br/>(Oceans, tides, mountains, weather)"] -->|"Abstracted to"| A2["Mathematical Point Mass<br/>(Position & Momentum)"] end

subgraph Computing["Computer Science"] C1["Digital Hardware & Execution<br/>(Chips, consoles, executing programs)"] -->|"Abstracted to"| C2["Mathematical Models<br/>(Functions & Behavioral Sequences)"] end ```

In computer science, reality consists of physical digital artifacts: processor chips, gaming consoles, or physical computers executing instructions. To reason about these digital realities cleanly, engineers must use formal mathematical models. While theoretical computer science contains many abstract structures—such as Turing machines and partially ordered sets of events—Lamport singles out two models as uniquely foundational and practical: - **Functions** - **Sequences of States**

[08:28](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=508s) The Function Model and Its Limitations

The simplest model for a program is a mathematical function that maps an input (or set of inputs) to an output. Lamport points out that a mathematical function is far simpler than a programming language "function" or procedure. In pure mathematics, a function is strictly defined as a set of ordered pairs. For instance, the function that squares natural numbers is the infinite set of pairs `{(0, 0), (1, 1), (2, 4),...}`. Writing `square(2) = 4` simply states that the ordered pair `(2, 4)` is an element of that set. To specify any function, one defines two aspects: 1. The **domain** (the set of all first elements in the pairs, such as the natural numbers, denoted as `Nat`). 2. The mapping rule for every value $x$ in that domain (mapping $x$ to $x^2$).

Lamport humorously distances himself from language design intricacies: “I'm not a programming language expert, so I don't know what those things that programming languages call functions are, but I know what math is, and math is a lot simpler.”

Despite its clarity, the functional model suffers from two major limitations: - **It omits operational behavior:** A function specifies *what* a program computes, never *how* it computes it. Quicksort and Bubblesort compute identical input-output functions, yet represent completely different algorithms. - **It cannot model reactive or non-terminating systems:** Systems such as operating systems never terminate to yield a final output pair; they run indefinitely (or until the sun explodes). A non-terminating system cannot be modeled as an input-to-output mapping.

[11:15](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=675s) The Standard Behavioral Model and Euclid’s Algorithm

To specify systems that execute over time or run indefinitely, Lamport turns to the **standard behavioral model**. Within this framework: - A **state** is defined as an assignment of values to variables. - A **behavior** is an execution modeled as a sequence of states. - A **program** is modeled as the set of all its possible behaviors (every valid execution sequence).

To illustrate this with an algorithm (which Lamport characterizes as simply an “abstract program”), he examines Euclid's algorithm for finding the greatest common divisor (GCD) of two integers, $m$ and $n$. The algorithm initializes variable $x$ to $m$ and $y$ to $n$, continually subtracts the smaller value from the larger, and terminates when $x = y$, at which point both variables hold the GCD.

Given inputs $m = 12$ and $n = 18$, the algorithm yields a deterministic behavior consisting of a single sequence of three states:

| Step | State Assignment | Transition Action | |:--- |:--- |:--- | | **Initial State** | $x = 12,\; y = 18$ | Subtract smaller ($x$) from larger ($y$): $18 - 12 = 6$ | | **Second State** | $x = 12,\; y = 6$ | Subtract smaller ($y$) from larger ($x$): $12 - 6 = 6$ | | **Final State** | $x = 6,\; y = 6$ | $x = y$; algorithm stops with $\gcd(12, 18) = 6$ |

[13:04](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=784s) Safety and Liveness Properties

Lamport addresses how to formally describe arbitrary sets of behaviors. He cites a fundamental theorem: any set of behaviors $B$ can be expressed as the intersection (conjunction) of two distinct properties: a **safety property** and a **liveness property**.

Without getting bogged down in overly formal mechanics, Lamport provides the operational intuition that distinguishes them: - **Safety Properties:** A safety property is violated if and only if it fails at a specific, identifiable point in time during the execution behavior. Lamport points to partial correctness as a quintessential safety property: if a program finishes its execution and outputs an incorrect result, you can pinpoint the exact moment of failure—namely, the state where the program terminated with the wrong value. - **Liveness Properties:** A liveness property asserts that something good must eventually occur. Unlike safety, a liveness property cannot be declared violated at any finite step; you must observe the complete, infinite behavior to determine whether it failed. Lamport identifies termination as the classic liveness property: at any finite moment during execution, one cannot assert that the program will never terminate, because it might still terminate in a subsequent step.

[14:18](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=858s) Specifying Safety Properties with Mathematics

A set of behaviors cannot generally be evaluated simply by inspecting a finite snippet of execution; for instance, determining that a system never terminates requires looking across its entire behavior. Behaviors are fundamentally specified using two distinct types of properties: safety and liveness. In practice, safety is far more critical because that is where errors are most likely to lurk and where bugs tend to be much more subtle. For the sake of time, Leslie Lamport sets liveness aside to focus exclusively on safety properties.

Specifying a safety property requires exactly two ingredients: the set of possible initial states, and a next-state relation that describes all admissible successor states from any given state. When considering what language should express these foundations, Lamport advises acting like scientists, pointing out that the universal language of science is mathematics.

```mermaid flowchart TD accTitle: Safety Property Specification accDescr: Diagram showing how an initial predicate and next-state relation generate state transitions. subgraph Spec["Safety Specification"] Init["Initial Predicate: Init"] Next["Next-State Relation: Next"] end subgraph Execution["Execution Behavior"] S0["State 0: (x, y)"] S1["State 1: (x', y')"] S2["State 2: (x'', y'')"] end Init --> S0 S0 -->|"Next(x, y, x', y')"| S1 S1 -->|"Next(x', y', x'', y'')"| S2 ```

In mathematical specifications, both components are written as formulas. Unprimed variables denote values in the current state, while primed variables denote values in the successor state. Lamport illustrates this using Euclid’s algorithm: - The initial state predicate: $x = m \land y = n$. Only a single initial state satisfies this relation once the parameters $m$ and $n$ are chosen. - The next-state relation: a disjunction handling the two algorithmic branches: $$(x > y \land x' = x - y \land y' = y) \lor (y > x \land y' = y - x \land x' = x)$$ - “Now in practice specifying safety just turns out to be more important because that's where errors are most likely to occur and tend to be more subtle,” Lamport notes.

[17:29](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1049s) Tracing Euclid's Algorithm and Understanding Non-Determinism

To demonstrate how formal formulas generate actual executions, Lamport walks through a concrete evaluation of Euclid's algorithm with concrete values: $m = 12$ and $n = 18$. Substituting these into the initial state predicate yields the single starting pair $x = 12$ and $y = 18$. The next state is then computed by plugging these values into the next-state formula:

| Step | Current State ($x, y$) | Next-State Evaluation | Next State ($x', y'$) | Action | |:--- |:--- |:--- |:--- |:--- | | **0** | $x = 12, y = 18$ | $12 > 18$ is false; $18 > 12$ is true. Formula simplifies to $x' = 12 \land y' = 18 - 12$. | $x = 12, y = 6$ | Step 1 | | **1** | $x = 12, y = 6$ | $6 > 12$ is false; $12 > 6$ is true. Formula simplifies to $x' = 12 - 6 \land y' = 6$. | $x = 6, y = 6$ | Step 2 | | **2** | $x = 6, y = 6$ | Both $6 > 6$ guards evaluate to false. Entire next-state relation becomes false. | None | Halts |

Because no possible values of $x'$ and $y'$ can satisfy the relation when $x = y = 6$, there is no next state, which signifies mathematically that the program has cleanly terminated.

Lamport highlights that Euclid’s algorithm is strictly deterministic because for every state $(x, y)$, there is at most one pair of successor values $(x', y')$ that makes the next-state relation true. Modeling non-determinism does not require any special or esoteric mechanics; it simply means having a next-state relation where a single current state allows multiple valid assignments to the primed variables. “Multiple assignments of values to primed variables that make next true for a single assignment of values to unprimed variables—that's what non-determinism is all about,” Lamport clarifies, demystifying the concept.

[20:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1240s) Formalizing in TLA+ and Industrial Impact at Amazon and Xbox

Formal languages exist primarily because tools require them. While mathematicians historically relied on informal mathematical notation, running an automated solver like Mathematica demands conforming to its precise syntax. Similarly, to check models or verify proofs mechanically, Lamport introduces TLA+. In TLA+, the mathematical definition of Euclid's algorithm is wrapped inside a module (`MODULE Euclid`) with explicit declarations: declaring $m$ and $n$ as constants, declaring $x$ and $y$ as variables, and importing arithmetic operators by extending the standard `Integers` module.

```mermaid flowchart LR accTitle: TLA+ Tooling Ecosystem accDescr: Flow from a TLA+ specification to model checking or mechanical proof verification. Spec["TLA+ Specification"] Spec -->|"Instantiate Constants"| TLC["TLC Model Checker"] TLC --> Check["Exhaustive State Exploration"] Spec -->|"Write Proofs"| TLAPS["TLA+ Proof System"] TLAPS --> Verified["Mechanically Checked Correctness"] ```

TLA+ allows users to exhaustively model check all possible executions of a design on small instances in a matter of nanoseconds, or to write formal proofs that are verified mechanically by a theorem prover. Lamport mocks the prevailing software engineering prejudice that “math works only for toy examples” and that modeling “real” systems requires programming constructs like types, procedures, and object-oriented abstractions, flatly answering: “Wrong.”

To dispel that myth, Lamport shares real-world evidence from industry adoption: - **Amazon Web Services (AWS)**: Lamport quotes Amazon engineer Chris Newcomb: “We have used TLA+ on 10 large complex real-world systems. In every case, TLA+ has added significant value, either preventing subtle serious bugs from reaching production or giving us enough understanding and confidence to make aggressive performance optimizations without sacrificing correctness.” Lamport underscores that contrary to claims that formal methods destroy performance or that management will forbid them, Amazon managers actively allocate engineering time during annual planning to write TLA+ specifications. - **Xbox 360 Memory System**: Microsoft researcher Chuck Thacker had an intern write a TLA+ specification of the Xbox 360 cache coherence and memory system. Merely writing down the formal specification—without even running the model checker—surfaced a subtle concurrency bug. When presented with it, the memory system’s architects at IBM acknowledged that the bug had completely eluded their hardware test suites and would have caused every single Xbox 360 console in the world to crash after approximately four hours of operation.

[25:08](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1508s) The Limits of Formalization: Informal Specs and TLATeX

While TLA+ is extraordinarily potent, Lamport emphasizes that today's talk is not about TLA+ itself, but about the necessity of *informal* specifications. To illustrate why informal specifications are indispensable, he turns to `TLATeX`, the pretty-printer tool he built for typesetting TLA+ formulas into LaTeX.

```mermaid flowchart TD accTitle: Pretty-Printer Decision Process accDescr: How TLATeX must infer user intent for formula alignment. Input["ASCII Input Formula"] Heuristic{"Align symbols or keep naive flow?"} Input --> Heuristic Heuristic -->|"User intended alignment"| Pretty["Aligned Mathematical Formatting"] Heuristic -->|"Alignment was accidental"| Naive["Naive Linear Output"] Pretty --> Subjective["Correctness = What the user wanted"] Naive --> Subjective ```

When pretty-printing mathematical text, a naive translation simply replaces ASCII tokens (such as `/\` with $\land$). However, alignment carries syntactic and semantic weight in TLA+: - If a user writes an indented list of conjunctions and alignment between equality signs, they almost certainly intended for those operators to line up neatly in the typeset output. - Conversely, in other formulas, identical vertical column alignments may be purely coincidental, and forcing an aligned tabular layout would distort the author's visual intent into an ugly, unintended layout.

The fundamental dilemma is that there is no rigorous mathematical definition for what constitutes "correct" aesthetic alignment; correctness is entirely subjective and boils down to whatever the user wanted. Because human desires cannot be captured in a rigorous mathematical formula, it is impossible to write a formal correctness specification for such a program. Yet Lamport firmly rejects the excuse that an absence of formal rules justifies jumping straight into code without design: “Not knowing what a program should do doesn't mean you don't have to think, you just go to code—it means you have to think a lot harder.” When requirements are messy, subjective, and undefined, writing an informal specification is more vital than ever.

[27:19](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1639s) Specifying a Pretty-Printer: Six Rules in Mathematical Prose

Faced with the challenge of formatting text where it is initially unclear what the formatting even ought to look like, Leslie Lamport explains that writing is an indispensable tool for thinking. To design his pretty-printer, he wrote a specification consisting of just six rules and accompanying definitions written as informal mathematical prose inside comments. Reading one of the rules aloud—“a left comment token is left comment aligned with its covering token”—Lamport admits with a laugh that he no longer remembers what it means without checking the spec, yet terms like “left comment aligned” and “covering token” were mathematically defined concepts. Formulating the system this way paid off immediately: it proved far easier to understand and debug six high-level rules through examples and dedicated debugging instrumentation than to debug 850 lines of code (which likely would have ballooned even further without the specification).

Lamport addresses why he chose informal mathematical prose rather than a formal TLA+ specification for the pretty-printer. First, the stakes were low: the world will not collapse if a comment is slightly misaligned in a pretty-printer, and formatting inherently lacks absolute corner-case perfection. Second, formal tooling like model checkers would not have been useful because there were no crisp invariant properties to verify against. This example illustrates what is typical of specifications: it sits at a higher level of abstraction than the code, it can be implemented in any programming language, and no popular programming methodology would have helped write it or eliminated the need for it. As Lamport emphasizes: “You write a spec to help you think about the problem before you think about the code.”

Conversely, what made this pretty-printer spec atypical is that it is unusually subtle—since 95% of everyday code requires far less thought—and it was expressed as an axiomatic set of rules. A set of rules or axioms is generally a poor choice for a specification because rule interactions are notoriously difficult to understand, but it happened to fit this problem well. Lamport draws a fundamental contrast between problem types: “Specifying what the pretty printer should do is hard; implementing the spec was easy. Specifying what a sorting program should do is easy; figuring out how to implement it efficiently is hard, at least if nobody has showed you how.”

- The pretty-printer specification comprised six core rules plus definitions written in comments as informal mathematical prose. - Designing the rules first kept the implementation down to approximately 850 lines of code. - Formal TLA+ and model checking were skipped because total correctness across every corner case was non-critical and no automated checker could evaluate aesthetic layout choices. - Standard programming methodologies offer no assistance in conceptualizing high-level domain rules prior to coding.

[32:03](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1923s) Quicksort and the Trap of Pseudocode

To illustrate how specifications guide algorithms that are difficult to implement efficiently, Lamport turns to Tony Hoare's Quicksort. Designed as a divide-and-conquer algorithm to sort an array $A[0 \dots n-1]$ of numbers, Quicksort relies on a partition procedure. Given bounds `low` and `high`, the partition procedure selects a pivot index in the range $\text{low} \dots \text{high}-1$ and permutes the sub-array so that every element up to the pivot is less than or equal to every element from the pivot plus one upward. While the clever internal mechanics of partitioning represent the real trick of Quicksort, Lamport abstracts that step away to focus entirely on the overall control structure.

```mermaid flowchart TD accTitle: Recursive Quicksort Flow accDescr: Standard divide-and-conquer recursion tree for quicksort down to base cases.

subgraph RecursiveCall["Recursive Step: qs(low, high)"] Cond{"low < high?"} Part["pivot = partition(low, high)"] Left["qs(low, pivot)"] Right["qs(pivot + 1, high)"] Done["Done (single element)"] end

Cond -- "Yes" --> Part Part --> Left Part --> Right Cond -- "No" --> Done ```

When written in standard pseudocode, Quicksort defines a recursive procedure `qs(low, high)`: if `low < high`, it invokes `partition(low, high)` and then recursively invokes `qs` on the two resulting sub-intervals; if not, it terminates. The main program simply calls `qs(0, n - 1)`. Lamport acknowledges that this informal pseudocode is perfectly easy to understand and straightforward to translate into almost any programming language. However, he warns that it hides a fundamental conceptual flaw: “Recursion is not a fundamental part of quicksort; it's just one way of implementing divide and conquer.”

- Quicksort partitions an array $A[0 \dots n-1]$ around a chosen pivot point between `low` and $\text{high}-1$. - Pseudocode specifications rely heavily on recursive procedure calls and base-case checks. - Recursive formulations obscure alternative execution models, particularly when attempting to parallelize across multiple CPU cores.

[34:49](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2089s) Thinking Above the Code: A Non-Recursive Quicksort in Math and TLA+

Lamport poses a classic challenge: ask programmers or colleagues to write a non-recursive version of Quicksort, and almost nobody can produce a working solution in ten minutes standing on their feet. Because their thinking is trapped inside code, they immediately attempt to manually compile the recursion by simulating a call stack. The true mathematical solution, however, is simple: maintain a set $U$ of index ranges that still require partitioning, initialized to contain the single pair representing the full range, $\{ (0, n-1) \}$. Described as a state machine with an `Init` predicate and a `Next` relation, $A$ is initially any array of numbers of length $n$, and $U = \{ (0, n - 1) \}$.

```mermaid flowchart LR accTitle: Set-Based Iterative Quicksort Transition accDescr: State transition relation showing removal of an interval from set U and addition of sub-intervals.

subgraph StateUpdate["Next-State Relation"] U_Old["U contains (B, T)"] Check{"B = T?"} BranchWork["Pick pivot P<br/>Permute A to A'<br/>U' = (U \ {(B, T)}) ∪ {(B, P), (P+1, T)}"] BranchBase["A' = A<br/>U' = U \ {(B, T)}"] end

U_Old --> Check Check -- "B ≠ T" --> BranchWork Check -- "B = T" --> BranchBase ```

The next-state relation specifies transitions between current values $(A, U)$ and next values $(A', U')$. As long as $U \neq \emptyset$, the algorithm non-deterministically selects any pair $(B, T) \in U$. If $B \neq T$, it picks a pivot index $P \in [B, T - 1]$, updates $A'$ to any valid permutation yielded by partitioning within those bounds, and updates $U'$ by removing $(B, T)$ and adding the two sub-intervals $(B, P)$ and $(P + 1, T)$. If $B = T$, no sorting is needed: $A'$ remains unchanged and $(B, T)$ is simply excised from $U$. Lamport underscores why this formulation is superior: “Why can almost no one find this version of quicksort? Because people's minds are stuck in code; they haven't learned to think at a higher level than the code.”

This mathematical specification is substantially more general than the textbook recursive algorithm: the execution traces generated by recursive Quicksort represent merely a strict subset of the valid behaviors allowed by this set-based specification. Because picking arbitrary elements translates directly into existential quantification ($\exists$) and state updates map to set operations, the entire algorithm can be expressed cleanly as a formal TLA+ formula. For engineers who prefer an algorithmic presentation over pure formulas, Lamport introduces PlusCal. PlusCal appears syntactically like a toy programming language embedded inside comments of a TLA+ module, but because any expression within it can be an arbitrary mathematical TLA+ expression, it provides vastly greater expressive power than ordinary programming languages.

- Iterative Quicksort maintains an outstanding work set $U$ containing interval pairs $(B, T)$. - The initial state relation defines $A$ as an array of length $n$ and $U = \{ (0, n - 1) \}$. - The next-state relation $Next$ operates non-deterministically on any element in $U$ until $U = \emptyset$. - The recursive version is merely one specific execution schedule among many valid behaviors permitted by the set-based specification. - PlusCal embeds algorithmic control structures inside TLA+ modules while allowing full mathematical expressions within statements.

[40:51](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2451s) PlusCal, TLA+, and the Need for Model Checkers in Concurrent Systems

Lamport reflects on the expressive power of PlusCal, explaining that it compiles cleanly into an easy-to-understand TLA+ specification. In his own workflow, he regularly writes algorithms in PlusCal, but when it comes time to verify their correctness, he reasons directly about the resulting TLA+ translation using formal tools like the TLC model checker and the TLA+ proof system. He shifts the discussion from simple programs that compute a finite mathematical function to programs that run forever—such as operating systems and distributed systems.

These long-running systems inherently involve concurrency, an area where human intuition routinely fails. Lamport candidly admits his own limitations when tackling them unaided: “Few people can get them right by just thinking and writing; I'm not one of them.” Because human reasoning alone cannot reliably master concurrent interactions, automated verification tools become non-negotiable. He points out that the TLC model checker was the decisive reason TLA+ proved so immensely valuable to engineers at Amazon. For any concurrent or distributed algorithm, writing a specification and thoroughly model checking it is essential to achieving correctness.

```mermaid flowchart TD accTitle: Workflow from PlusCal to Verified Implementation accDescr: An algorithm is written in PlusCal, translated to TLA+, verified via model checking and proofs, and then implemented.

subgraph SpecPhase ["Specification and Verification"] PC["PlusCal Algorithm"] --> Trans["TLA+ Translation"] Trans --> Tools["TLA+ Tools:<br/>Model Checker (TLC) & Theorem Prover"] end

subgraph CodePhase ["Implementation"] Tools --> Impl["Implementation Code<br/>(Operating & Distributed Systems)"] end ```

- Programs that run indefinitely almost always involve concurrency and distributed components. - Amazon Web Services engineers successfully leveraged the TLA+ model checker to uncover subtle bugs in critical distributed systems. - PlusCal provides an algorithmic syntax resembling pseudocode, while translating into pure TLA+ for formal verification.

[42:14](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2534s) The Discipline of Thinking: Writing Down What Code Does

Turning to the other 95% of software engineering—the seemingly straightforward code—Lamport shares a concrete example from his own programming. He wrote a specification for a module simply to confirm he truly understood what the code had to do before typing out a single line of implementation. Without writing it down, one only assumes the behavior is obvious. Committing it to a specification forced him to confirm that it was genuinely as simple as he imagined. Furthermore, writing it paid off later: whenever he needed to interact with that module down the road, he did not need to decipher the underlying code; he merely read the concise specification.

From this experience, Lamport extracts a fundamental rule for software specifications: a specification describing what a piece of code does should articulate everything anyone needs to know in order to use that code. In his example, the internal mechanism was too trivial to warrant an elaborate internal spec. Had it turned out to be unexpectedly intricate during coding, he would have halted coding immediately and returned to writing a spec. He challenges the audience on human nature and problem-solving: “Everyone thinks they think, but if you don't write down your thoughts, you're fooling yourself.” To think before you code fundamentally means to write before you code, because a specification is simply what you write before coding.

- Any code that another person—or the original author a month later—might need to use or modify warrants a specification. - A specification can cover an entire architecture, a subsystem, a single class, a method, or even a tricky algorithm nested inside a method. - Specifications must document *what* the code does (interfaces, preconditions, outputs), and should detail *how* it does it only when the underlying algorithm is subtle and complex.

[44:50](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2690s) Mathematical Thinking, Comments, and the Separation of Coding

Lamport argues that programmers must learn to view their systems above the code level, expressing designs in terms of discrete states and behaviors, or mathematical functions mapping inputs to outputs. Regardless of whether a specification is written in formal mathematical syntax, informal prose, or high-level pseudocode, the underlying thought process must remain mathematical. When asked how one learns this art, Lamport insists that the most effective route to writing good informal specifications is learning to write formal ones first. Just as a programmer learns to code by writing programs, running them, and fixing bugs, one learns to specify by writing formal specs, running them against a model checker, and correcting the errors that the tool exposes.

While TLA+ may not be the optimal formal language for every domain, Lamport maintains it is uniquely effective for teaching engineers how to think mathematically. Once a specification exists, it must be connected to the codebase. This bridge belongs directly in the program comments, mapping mathematical abstractions to concrete data structures—for instance, noting that a mathematical graph is realized as an array of node objects and an array of link objects. Lamport stresses that he has no dogma regarding programming languages, frameworks, or coding methodologies: “Writing a spec isn't going to catch coding errors because it's not about coding; it'll catch algorithm errors.” Coding remains a separate discipline where testing and debugging are still necessary, but an upfront specification prevents foundational design flaws and makes programs vastly superior.

- Formal specs build the intellectual rigor needed to write clear, unambiguous informal specifications. - Model checkers provide immediate feedback on specifications analogous to compilers and test suites for source code. - Specs and the explicit mapping of mathematical concepts to implementation variables should live inside the source code comments. - Upfront specifications do not replace unit testing or debugging, but they eliminate algorithmic mistakes before code is ever written.

[48:03](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2883s) Why Writing Is Hard, the Reality of Patches, and Eisenhower's Lesson

Programmers frequently resist writing specifications because writing is intellectually demanding. Lamport observes that writing is hard precisely because thinking is hard, humorously paraphrasing the ancient maxim often attributed to Archimedes or Euclid that there is no royal road to mathematics. Writing acts like running: if you do not practice, your writing muscles atrophy, making it tempting to find excuses to avoid it altogether. The most pervasive excuse is the certainty of change: requirements shift, features are added, specifications contain flaws, and code inevitably diverges from the original plan. In the real world, programs are patched rather than redesigned from scratch.

Even so, Lamport insists that writing an upfront specification is vital for two reasons. First, anyone tasked with maintaining the codebase later—often the original author—will be deeply grateful for every word and formula left behind. Second, patching code degrades it: “Every time code is patched, it becomes a little uglier, harder to understand, and harder to maintain; and if you don't start with a spec, every piece of code you write is a patch.” Without a baseline design, software starts its life as an ugly patch on day one. Invoking Dwight D. Eisenhower's famous military dictum—that no battle was ever won according to plan, but no battle was ever won without one—Lamport cautions the audience against those who dismiss specifications: “Thinking doesn't guarantee that you won't make mistakes, but not thinking generally guarantees that you will.”

- Useful programs inevitably attract users whose evolving requirements force patches and modifications. - When modifications occur, programmers should strive to update the specification alongside the code. - Starting without an explicit specification ensures the codebase is disorganized and difficult to maintain from inception. - Lamport directs interested practitioners to his Microsoft Research homepage for links, documentation, and tools related to TLA+.

[52:15](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3135s) Q&A: Specification by Example and Test-Driven Development

Following the presentation, Lamport opens the floor to a brief question-and-answer session. An audience member raises a question about pragmatic industry trends, asking Lamport for his view on "specification by example," an approach closely tied to test-driven development (TDD) where concrete usage examples serve as the functional specification under the justification that formal completeness is undecidable anyway.

Lamport rejects the premise outright, calling specification by example a "terrible pragmatic approach." While he readily concedes that concrete examples are wonderful pedagogical tools for explaining a concept to a human being, he argues that relying on them as a specification actively prevents genuine engineering analysis. Concrete examples confine the developer's mindset to convenient scenarios. The central challenge of programming, Lamport argues, is not designing for the obvious, straightforward cases, but anticipating and correctly handling the rare, difficult edge cases where systems break down.

[53:19](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3199s) The Failure of Specification by Example

Leslie Lamport wraps up his critique of relying on code examples in place of precise specifications by sharing his personal frustrations with Eclipse plugin development. He explains that countless methods in Eclipse are documented merely with an example of code calling them. In his experience, attempting to adapt those methods to do what he actually wants almost never succeeds because the code only works for the narrow, cherry-picked scenario demonstrated in the documentation. Lamport emphasizes that while he cannot speak definitively on the viability of programming by example, substituting examples for genuine specifications fundamentally fails to communicate what a system does. As he dryly summarizes: “Programming by example I don't know about you know if that works, but specification by example, it doesn't.”

[54:09](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3249s) Patching Code and Preserving Old Specifications

An audience member asks what engineers should do with older specifications once a bug is patched in the code, questioning whether it is useful to retain historical context or if old specs should simply be discarded. Lamport explains his own workflow, noting that whenever fixing a bug requires altering a specification, he makes sure to record detailed notes explaining exactly why the change was made, anticipating that years later he will return to the code and wonder what prompted the modification.

On the broader question of version retention, Lamport advocates strongly against throwing past documentation away, pointing out that modern digital storage makes retaining history virtually cost-free: “It doesn't cost anything to save it, so you should certainly save the old specs.” However, whether developers should explicitly document every historical change inside the text of the new specification itself remains an open design choice for which he does not claim a single definitive answer.

[55:17](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3317s) The 95% Versus the Critical 5%: Why Undocumented Code Reuse Fails

A questioner with a PhD in formal methods challenges Lamport by observing that modern software development has moved away from formal specifications. Instead, 99% of contemporary software engineering involves copying existing algorithms and libraries, stitching them together, and tweaking them to fit immediate needs.

Lamport responds that while building software that way sounds appealing in theory, in practice it breaks down precisely because the third-party components lack rigorous specifications. Programmers end up wasting immense amounts of time reverse-engineering what external libraries actually do under the hood. While Lamport concedes that 95% of ordinary code requires very little formal specification—often just a few explanatory lines—engineers will inevitably hit a critical 5% that involves subtle concurrency or deep architectural difficulty:

> “There's gonna be something, some things, code you're gonna have to write that is subtle, it has some a bit of difficulty in it, and you want to be able to, you know, you want to be prepared for that, because we use the same techniques that you use, you know, the same lack of thought that you use when you're doing that other 95%, you're gonna screw up badly.”

[57:12](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3432s) TLA+ and the "Evil" of Temporal Logic

An attendee asks Lamport about the theoretical foundations of his work, noting that anyone familiar with temporal logic will recognize modal concepts like future and necessity operators running beneath his presentation, and asks whether those operators could be used more directly. Lamport clarifies that TLA stands directly for the "Temporal Logic of Actions." He then offers a candid critique of the formalism, declaring: “Temporal logic is evil.” He explains that it is exceptionally hard to work with and reason about directly, yet it remains a necessary evil because it provides the best known formalism for handling liveness properties (the guarantee that something good eventually happens).

Critically, Lamport points out that safety properties (guaranteeing that nothing bad happens) are far more essential in real-world systems, and safety requires no temporal logic whatsoever. The key design breakthrough of TLA+ is that it insulates the engineer from temporal operators almost entirely:

> “The thing that makes it great is that you'll write a thousand-line specification, 999 lines are going to be ordinary math—initial predicate, next-state relation—and then the last line is going to be a temporal logic formula.”

```mermaid flowchart TD accTitle: Anatomy of a TLA+ Specification accDescr: Diagram illustrating how 999 lines of ordinary math define safety before a single line of temporal logic defines liveness.

subgraph OrdinaryMath["Ordinary Mathematics (999 Lines)"] Init["Initial Predicate (Init)"] Next["Next-State Relation (Next)"] Safety["Safety Guarantees: 'Nothing bad happens'"] Init --> Safety Next --> Safety end

subgraph TemporalLogic["Temporal Logic (1 Line)"] Live["Liveness Formula: 'Something good eventually happens'"] end

Safety -->|"Combined into spec"| TLASpec["Complete TLA+ Specification"] Live -->|"Appended at the end"| TLASpec ```

- **TLA name origin**: TLA stands for the *Temporal Logic of Actions*. - **Temporal logic usability**: Described as very difficult to use directly, but indispensable for formalizing liveness. - **Spec ratio**: In a typical 1,000-line TLA+ specification, roughly 999 lines consist of standard discrete mathematics defining initial states and state transitions, with only the final line invoking temporal logic operators.

[59:16](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3556s) Plenary Session Conclusion

The session chair steps up to close the plenary session and thanks Lamport for his presentation. Before dismissing the attendees, the organizer delivers logistical announcements regarding evening dinner plans, instructing participants to collect their bags and board the waiting buses outside, which will transport everyone to the dinner venue and subsequently return guests to their respective hotels. The talk concludes with a round of applause for Lamport.

## Notable quotes > [48:03](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2883s) Every time code is patched, it becomes a little uglier, harder to understand, and harder to maintain; and if you don't start with a spec, every piece of code you write is a patch. — Leslie Lamport

> [48:03](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2883s) Thinking doesn't guarantee that you won't make mistakes, but not thinking generally guarantees that you will. — Leslie Lamport

> [02:10](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=130s) If you're thinking without writing, chances are you're fooling yourself, we're only pretending to think. — Leslie Lamport

> [27:19](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1639s) You write a spec to help you think about the problem before you think about the code. — Leslie Lamport

> [44:50](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2690s) Writing a spec isn't going to catch coding errors because it's not about coding; it'll catch algorithm errors. — Leslie Lamport

> [04:10](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=250s) If you're going to use tools, it means you need a formal language, because tools don't understand prose. — Leslie Lamport

> [32:03](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1923s) Recursion is not a fundamental part of quicksort; it's just one way of implementing divide and conquer. — Leslie Lamport

## Mentioned Amazon Web Services (AWS), Brandeis University, Chris Newcomb, Chuck Thacker, Compaq, Digital Equipment Corporation (DEC), Dwight D. Eisenhower, Eclipse, IBM, Judith Bishop, LaTeX, Leslie Lamport, Microsoft Research, MIT, PlusCal, Richard Guindon, SRI International, TLA+, TLC, Tony Hoare, Xbox 360

## Video description Architects draw detailed blueprints before a brick is laid or a nail is hammered. Programmers and software engineers seldom do. A blueprint for software is called a specification. The need for extremely rigorous specifications before coding complex or critical systems should be obvious—especially for concurrent and distributed systems. This talk explains why some sort of specification should be written for any software.

http://www.microsoftfacultysummit.com

[Source](https://www.youtube.com/watch?v=-4Yp3j_jk8Q)

中文简报

兰伯特呼吁数学规范,亚马逊与Xbox借TLA+防崩溃 不写规范就直接编码的软件工程师是在思考上自欺欺人,这注定了他们写下的每一行代码从一开始就是难以维护的补丁。 图灵奖得主莱斯利·兰伯特(Leslie Lamport)指出,软件开发之所以饱受困扰,是因为工程师们没有先在纸上把设计想清楚就急于编写代码。他提出了一个涵盖从简短散文到如TLA+等严谨数学公式的规范连续谱,展示了状态机和基础数学概念如何在不受编程语言偏差干扰的情况下清晰描述系统。兰伯特结合非递归快速排序、文本美化排版,以及亚马逊云科技(AWS)和Xbox 360的工业应用等具体实例,阐明了规范安全属性能够防止代码测试所遗漏的严重架构缺陷。他最终主张,写作是进行清晰思考唯一可靠的机制,并敦促开发者将编码视为架构设计下游的具体实现细节。 莱斯利·兰伯特主张软件开发者应学习建筑师的素养,在编码前先撰写规范,并强调动笔之外的所谓思考不过是假装思考。 (2:10) 像TLA+这样的形式化规范方法已经产生了重大的工业影响,包括预防了亚马逊云科技的严重宕机事故,以及发现了Xbox 360内存架构中可能导致运行四小时后崩溃的硬件缺陷。 (20:40) 绝大多数软件的执行都可以使用由初始状态谓词和下一状态关系构成的标准行为状态机来建模,在处理安全属性时无需复杂的时序逻辑。 (14:18) 兰伯特坚决反对业界以“实例化规范”和测试来替代形式化规范的做法,他指出示例会掩盖边缘情况,而在缺乏行为规范时,第三方库的复用往往以失败告终。 (52:15)

Reading summary

Turing Award laureate Leslie Lamport argues that software developers must cultivate the discipline of thinking and writing specifications above the code level before implementation, using mathematics to prevent architectural flaws. Writing as Disciplined Thinking and Specification Spectrum (2:10) Lamport contends that thinking serves as humanity's primary tool for handling high-stakes endeavors, but it must occur before action begins—likening programming without prior design to building a house while carpenters nail down the roof. Quoting cartoonist Richard Guindon, Lamport asserts that writing exposes sloppy thinking, warning that engineers who attempt to reason purely inside their heads are largely pretending to think. Software blueprints, which he defines as specifications, span a continuum rather than requiring a single rigid formalism: short prose suffices for routine code, mathematical prose clarifies tricky logic, and formal languages become necessary when software tools are required to verify mission-critical designs. Modeling Systems Through Functions and State Sequences (6:40) Drawing parallels to astronomy, where complex physical planets are abstracted into simple point masses defined by position and momentum, Lamport advises computer scientists to model digital hardware and execution cleanly. The functional model maps inputs to outputs as sets of ordered pairs, which Lamport notes is suitable for terminating computations but inadequate for describing operational behavior or non-terminating reactive software. To handle indefinite execution, Lamport turns to the standard behavioral model, defining a state as variable assignments and an execution behavior as a sequence of states. Behaviors are governed by two distinct classes of properties: safety (asserting nothing bad happens, verifiable at a specific finite step) and liveness (asserting something good eventually occurs, observable only over infinite behaviors). In practice, Lamport concentrates on safety, noting that it harbors the most subtle errors. Safety is modeled mathematically using an initial state predicate and a next-state transition relation over unprimed and primed variables, which naturally captures non-determinism whenever a single current state allows multiple valid successor states. Industrial Impact and Practical Abstraction Choices (20:40) To mechanically verify designs, Lamport developed TLA+ and the algorithmic language PlusCal, directly challenging the industry prejudice that mathematics applies only to toy problems. Real-world deployments demonstrate significant benefits: Amazon Web Services applied TLA+ across 10 large systems to catch critical bugs and safely execute aggressive optimizations, while an intern's TLA+ specification of the Xbox 360 memory system surfaced a subtle cache coherence defect that had eluded IBM's hardware testing and would have caused consoles to crash after four hours. Formalization is not universally applicable, however. When building the TLATeX pretty-printer, Lamport instead utilized six axiomatic rules in informal mathematical prose. Because aesthetic token alignment reflects subjective human preference rather than mathematically verifiable correctness, formal model checkers offered no utility. The upfront informal spec nonetheless prevented code bloat, keeping the implementation to approximately 850 lines. Algorithmic Control and the Limits of Code Reuse (32:03) Lamport critiques recursive pseudocode formulations of Tony Hoare's Quicksort, arguing that recursion is merely one implementation choice that blinds programmers to alternate control flows. By abstracting the algorithm mathematically to a state machine maintaining a set of unfinished intervals, execution traces generalize far beyond standard recursion and map cleanly to parallel execution. Programmers often struggle to write non-recursive versions because their thought processes remain trapped within code syntax. Addressing modern industry patterns, Lamport dismisses 'specification by example' and test-driven development as inadequate substitutes for specifications, recounting how example-only documentation in Eclipse plugins consistently failed when adapted beyond narrow scenarios. While Lamport concedes that roughly 95% of everyday code requires minimal formal specification, he cautions that developers will inevitably encounter a critical 5% featuring intricate concurrency or subtle distributed interactions where a lack of mathematical rigor leads to catastrophic system failures. Lamport acknowledges that writing specifications is intellectually demanding and will not eliminate coding bugs, but he maintains that an ungrounded codebase degenerates into an accumulation of patches. While he characterizes temporal logic as a difficult yet necessary tool for liveness, he notes that TLA+ confines modal operators to the final line of a specification, relying on standard discrete mathematics to secure essential safety properties.

精炼阅读摘要

图灵奖得主Leslie Lamport在微软学术峰会上提出,软件工程师必须建立“超越代码的思考”,在动笔写代码前通过书写规约(Specification)来理清系统设计;未经书面记录的思考往往只是自欺欺人,而数学则是消除复杂系统隐患的基础工具。 书写规约:代码动工前的建筑蓝图 (2:10) Lamport认为,正如猎人在走出洞穴前就必须规划好如何应对剑齿虎、建筑工人在打地基前必须备好建筑蓝图一样,程序员绝不能在敲击键盘的同时才去构想架构。他引用漫画家Richard Guindon的观点指出,写作是大自然揭示人类思维有多么粗糙的方式。如果不把思路清晰地写下来,大脑进行的所谓复杂推理往往只是假装思考。规约在软件工程中扮演着与工程蓝图完全相同的角色,不仅界定了程序应当具备的功能,也迫使开发者在实现前直面设计中的模糊之处。 工程师对规约常常抱有畏难情绪,误以为其必须充斥着生涩的学术符号与死板的形式化教条。Lamport澄清说,正如建筑图纸涵盖从简易手绘草图到摩天大厦设计图等多种类型,软件规约也呈现分级光谱:绝大多数普通代码只需几句简短散文即可讲清意图;逻辑较为精巧的部分需使用严谨的数学散文;唯有涉及深层并发或分布式等关键系统时,才必须使用工具能够识别的形式化语言。当代码需要修改或交给他人维护时,规约应当讲清使用该模块所需知晓的一切信息。 标准行为模型:以状态序列刻画系统 (6:40) 在计算机科学中,思考真实数字系统需要建立数学模型,这与天文学将包含复杂地貌与气候的行星抽象为由位置与动量定义的质点并无二致。纯粹的函数模型虽然清晰且数学结构简单,却存在两大致命缺陷:一是它只关注输入到输出的映射(“计算什么”),无法区分快速排序与冒泡排序等不同的运行机制(“如何计算”);二是它无法用于建模操作系统或分布式系统等永远不终止、持续响应外部输入的反应式系统。为此,Lamport推荐采用以状态机为基础的“标准行为模型”。 在该模型中,状态被定义为变量的取值赋值,系统的单次执行轨迹被抽象为由状态构成的序列(行为),而整个程序则是其所有允许出现的行为集合。任何行为集合都可以严格分解为安全性(Safety)与活性(Liveness)两种性质。安全性确保“坏事绝不发生”,其违规必然能在执行的某个确定有限步骤中被明确指出,例如程序最终输出了错误结果;活性则保证“好事终将发生”,例如程序最终会终止,这无法在任何有限步骤内判定违规。由于绝大多数隐藏且微妙的系统缺陷都集中在安全性上,系统建模的核心往往聚焦于两项数学要素:初始状态断言(Init)以及用带撇变量表示下一状态的状态转移关系(Next)。 TLA+形式化工具与工业级缺陷排查 (20:40) 人类依靠肉眼思考并发系统与分布式交互时极易出错,Lamport直言连他自己都无法脱离工具独自写对这类系统,因此严密的形式化建模与模型检测工具成为必需。他设计的TLA+(基于动作的时态逻辑)以及更高层级的算法语言PlusCal,允许工程师在小规模实例上穷举检查所有可能的状态转移,或借助定理证明器开展机械化证明。针对软件界所谓“数学方法只能对付玩具范例”的偏见,Lamport列举了工业界的实际案例予以驳斥。 亚马逊AWS在10个大型复杂现实系统中引入TLA+,不仅排除了未能进入生产环境的微妙隐患,还使工程团队能够在确知不牺牲正确性的前提下开展激进的性能优化,甚至为此在年度规划中特意预留形式化规约的时间。此外,微软在研发Xbox 360的缓存一致性与内存系统时,一名实习生为其编写了TLA+规约;在模型检测器尚未运行的情况下,仅梳理规约本身就直接暴露出一个极其隐蔽的并发死锁漏洞。负责硬件的IBM架构师承认,该缺陷完全绕过了他们原有的硬件测试集,若直接量产将导致全球所有主机在开机约4小时后集体崩溃。 超越代码实现:非递归快排的抽象启发 (32:03) 工程师往往容易被编程语言的控制流概念所困,无法在代码之上进行高层次思考。Lamport以Tony Hoare的快速排序算法为例:当要求程序员在10分钟内写出非递归版本时,绝大部分人会陷入用数组模拟函数调用栈的狭隘泥潭。这背后的根源在于伪代码教程将递归视为快排的本质,而忽视了递归仅仅是分治策略在语言层面的特定实现方式。 从数学本质上看,快速排序的状态机仅由待处理区间集合U与数组A构成。只要U中仍有区间,算法即可非确定性地选取其中一段进行划分,并将产生的子区间放回U中,直到U变为空集。教科书上的递归调用序列,仅仅是这种基于集合的状态转移模型所允许的诸多合法执行路径中的一种特例。这种超越语言语法的数学视角极大地拓宽了实现自由度,为在多核处理器上进行无锁并发调度与并行化提供了极高灵活度。 反思研发惯例:警惕以例子代规约与无序修补 (48:03) 在问答环节,Lamport直接批判了敏捷开发中与测试驱动开发(TDD)紧密绑定的“以例代规”(Specification by Example)。他认为具体示例适合用于向人类进行教学解释,但决不能作为规约使用。以示例代替规约会将开发者的视野限制在顺畅路径上,而编程的真正挑战恰恰在于处理那些边缘极端案例。他以编写Eclipse插件的亲身经历说明,那些只给出调用代码范例的方法往往在面对略微偏离官方范例的实际场景时完全失效。 针对“现代软件开发99%都在复用无规约第三方库”的提问,Lamport直言这种模式之所以痛苦,正是因为使用者不得不耗费大量时间逆向工程这些缺乏严谨说明的代码。虽然普通软件中95%的逻辑较为平庸,但工程师迟早会遭遇包含复杂并发与架构难题的5%关键代码。艾森豪威尔曾有名言“没有战役是按计划打赢的,但也没有战役能在没有计划的情况下获胜”;Lamport强调,需求演进带来的补丁是现实,但每一次无蓝图的硬编码修补都会让系统愈发丑陋难懂,缺乏前期思考的编码不仅不能省时,反而注定将系统从第一天起就置于补丁的泥沼中。 Lamport坦言时态逻辑虽然在处理活性性质时难以避免,但在工程实践中直接使用却极其棘手和痛苦,因此TLA+的核心突破在于将99.9%的规约转化为基础离散数学,仅在最后单行引入时态算子。虽然形式化工具无法直接捕捉主观审美或消灭普通编码层面的手误,但在编码前通过数学模型写就规约,是预防算法深层缺陷、降低软件全生命周期维护代价不可替代的核心思维训练。

Detailed notes — zh

## Background 该视频由微软研究院(Microsoft Research)于 2014 年 7 月 15 日发布在 YouTube 平台上,内容为图灵奖得主 Leslie Lamport 在微软学术峰会上的主题演讲。视频简介指出,建筑师在动工前会绘制详尽的蓝图,而程序员却鲜有这么做的习惯;演讲重点阐明了为什么在动笔写代码之前,任何软件系统(尤其是复杂的并发与分布式系统)都需要某种形式的严格规约(Specification)。 ## TL;DR 图灵奖得主 Leslie Lamport 提倡“超越代码的思考”,指出写作是检验思维是否草率的唯一标准,工程师应在编码前通过书写规约来厘清系统行为。他通过欧几里得算法、快速排序以及代码排版工具等生动案例,展示了如何运用数学中的函数模型与状态序列(安全性与活性属性)来建模软件。Lamport 不仅结合亚马逊云服务和 Xbox 360 的实战案例力证了 TLA+ 形式化验证在复杂系统中的威力,还强调非形式化的数学散文对于厘清逻辑同样不可或缺。他直言软件开发中最危险的 5% 核心逻辑绝不能依靠盲目拼凑代码或“按示例规约”来蒙混过关,唯有高维度的严谨抽象与书面思考才能构建出真正可靠的系统。 ## Atmosphere & style 整场演讲充满了一代大师直言不讳、举重若轻的学术锋芒与幽默感。Lamport 的语气自信而犀利,在调侃程序员习惯性逃避思考、沉迷于调试代码的同时,金句频出、切中要害。问答环节更是交锋激烈、毫不客套,面对观众抛出的流行概念(如按示例规约或过度使用时序逻辑),他均能一针见血地直指盲区,现场气氛在严肃的技术思辨与会心的大笑之间游刃有余。 ## Bulletin - [02:14](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=134s) 图灵奖得主 Leslie Lamport 强调“思考超越代码”,主张在动笔敲代码前必须通过写作建立软件蓝图(规约),否则脱离书面落笔的构想大多只是在自欺欺人。 - [03:34](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=214s) 规约存在宽广的光谱:从普通代码适用的简短散文,到精密逻辑的数学散文,再到针对并发与分布式等关键系统、必须配合模型检验器的严谨形式化语言(如 TLA+)。 - [20:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1240s) 形式化方法在工业界展现出巨大威力,亚马逊在 10 个大型复杂分布式系统中使用 TLA+ 大幅降低严重漏洞并支持激进优化,而微软实习生编写 TLA+ 规约时排查出了足以导致全球 Xbox 360 开机 4 小时必死机的硬件漏洞。 - [52:35](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3155s) Lamport 强烈批评“按示例规约”(Specification by Example),认为示例不仅阻碍抽象思考,而且根本无法替代规范来覆盖软件中最致命的边界极端情况。 ## Key takeaways - [02:14](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=134s) 真正的思考必须落在纸面上;正如漫画家 Richard Guindon 所言,写作是大自然让你明白自己的思维有多草率的方式。 - [03:34](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=214s) 软件规约就是程序的施工蓝图,其形态覆盖了从简短自然语言散文、数学散文到严格形式化规约的完整光谱。 - [06:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=400s) 计算机科学家应像自然科学家一样建立极简数学模型,其中最重要的两大支柱是函数模型(刻画输入到输出)与状态序列模型(刻画持续运行的行为)。 - [13:04](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=784s) 系统行为集合在理论上可严格分解为安全性(Safety,保证坏事永不发生)与活性(Liveness,保证好事最终一定会发生)的交集,而在工程实践中安全性往往更重要且更容易隐藏隐蔽错误。 - [14:18](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=858s) 规范安全性属性只需定义初始状态集合(Init)与下一状态关系(Next-state Relation),非确定性在数学本质上只是同一当前状态下存在多个满足公式的下一状态赋值而已。 - [20:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1240s) 形式化工具 TLA+ 能通过小模型遍历检验(Model Checking)穷举系统行为,它在亚马逊分布式系统和 Xbox 360 关键硬件中成功拦截了极端隐蔽的致命架构缺陷。 - [27:19](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1639s) 面对代码格式化工具这类无法用数学精准定义“何为主观正确”的问题,先提炼并调试 6 条非形式化数学散文规则,远比直接调试数百行实现代码高效得多。 - [34:32](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2072s) 程序员普遍陷入代码思维定式,无法在 10 分钟内手写出非递归快速排序,而站在数学状态转移视角(维护待分区区间集合 U)不仅推导清晰,而且天然支持并发。 - [39:23](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2363s) PlusCal 语言将伪代码语法与 TLA+ 数学表达式结合,不仅具备极高表达能力,还能直接编译为 TLA+ 规约供自动化工具检验。 - [40:51](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2451s) 并发与分布式系统极其复杂,人类无法仅靠肉眼和散文保证其正确性,必须撰写形式化规约并借助模型检验工具来排查漏洞。 - [47:07](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2827s) 在修改与维护代码时规约至关重要;正如艾森豪威尔的战役名言,如果不从规约开始设计,写下的每一行代码从第一天起就注定只是难看脆弱的补丁。 - [55:17](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3317s) 工业界 95% 的代码可能属于常规拼凑,只需简短文字即可阐明;但若将不动脑子的拼凑习惯套用到剩下 5% 极其精妙的关键核心代码上,系统必将彻底崩溃。 - [57:12](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3432s) 时序逻辑晦涩反直觉,是描述活性时不可避免的“必要之恶”;TLA+ 的精妙之处在于将 99% 的规约还原为普通的离散数学与谓词逻辑,仅在最后一步用极简的时序公式收尾。 ## Detailed walkthrough ### [00:01](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1s) 演讲开场与图灵奖得主介绍 Judith Bishop 登台主持微软学术峰会(Microsoft Faculty Summit)的大会主题报告环节,向全体与会者隆重介绍微软研究院硅谷实验室(Microsoft Research Silicon Valley Lab)的主任研究员 Leslie Lamport。Bishop 指出,凡是教过或学过操作系统课程的师生,甚至每一位使用现代计算机的人,都深深受益于 Lamport 奠基性的开创研究——包括逻辑时钟(Logical Clocks)、安全性与活性(Safety and Liveness)、顺序一致性(Sequential Consistency)以及操作系统的诸多关键领域。正是这些研究,为计算机系统从单台桌面电脑扩展到当今庞大的互联设备网络奠定了安全运行的基础。 在履历背景方面,Bishop 提到 Lamport 加入微软之前曾就职于麻省理工学院(MIT)、布兰迪斯大学(Brandeis University),以及 DEC、Compaq 和 SRI 等知名软硬件机构与企业,并在职业生涯中斩获了多所高校的名誉博士学位。Bishop 幽默地补充道,Lamport 曾长期致力于帮助科学家把工作呈现得更具美感,并为此发明了众所周知的排版系统 LaTeX;而今天,他将致力于说服各位工程师去领略生活中另一种极致的美——数学。随后,全场以热烈掌声欢迎当年度图灵奖(A.M. Turing Award)得主 Leslie Lamport 登台演讲。 - 主讲人职务:微软研究院硅谷实验室主任研究员(Principal Researcher) - 关键学术贡献:逻辑时钟、安全性与活性、顺序一致性、分布式系统与操作系统基础理论 - 软件与工具贡献:科学界广泛采用的高质量排版系统 LaTeX - 荣誉:ACM 图灵奖得主、多所高校名誉博士 --- ### [02:14](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=134s) 为何思考、何时思考与如何思考 Lamport 以“Thinking Above the Code”(超越代码的思考)为题切入核心演讲。他首先抛出一个基础问题:人类为什么需要思考?因为思考有助于人类完成绝大多数复杂事务,无论是远古时期猎杀剑齿虎、建造一栋房屋,还是编写一段计算机程序。紧接着是“何时思考”的问题:Lamport 风趣地指出,猎杀剑齿虎的最佳思考时机是走出洞穴之前,等到猛虎已经向你扑面飞奔而来时,思考显然为时已晚;建造房屋也是同理,你绝不希望在木工已经开始搭房顶时,才手忙脚乱地琢磨到底该怎么建。因此在编写软件时,程序员应该在动笔敲下任何一行代码之前就完成思考。 关于“如何思考”,Lamport 引用了他极其推崇的漫画家 Richard Guindon 的一句名言:“Writing is nature's way of letting you know how sloppy your thinking is”(写作是大自然让你明白自己的思维有多草率的方式)。他严肃地告诫在场工程师:要想真正地思考,就必须动手写作;如果脱离了书面记录单纯在大脑里打转,大概率只是在自欺欺人,或者只是在“假装思考”。 ```mermaid flowchart LR accTitle: Thinking and Writing Flowchart accDescr: An illustration showing that rigorous thinking requires formal or written documentation before physical implementation. subgraph Preparation["事前准备(思考与落笔)"] A["构思阶段<br/>Think"] --> B["书面化沉淀<br/>Write"] end subgraph Execution["实施阶段(构建实体)"] B --> C["付诸实现<br/>Code / Build"] end ``` --- ### [03:34](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=214s) 蓝图、形式化语言与规约的光谱 确定了必须落笔之后,接下来的问题是“该写些什么”。Lamport 调侃道,远古时代捕猎剑齿虎没法写,因为当时文字还没发明出来,所以那是一项极其危险的营生;而在建房时,施工前写下并绘制的东西被称为“蓝图”(Blueprints)。在软件工程中,程序的蓝图就是所谓的“规约”(Specification)。针对软件行业谈规约色变、一听到该词就感到恐慌的现状,Lamport 调侃称大家往往视其为某种不可阅读、充满诡异数学符号的形式化怪物。 但 Lamport 强调,蓝图本身存在一个宽广的光谱(Spectrum):一端是面对庞大复杂工程时必不可少的超高精度详细蓝图;另一端是周末随手搭个简易木棚时寥寥数笔的草图;中间则是建造普通民居时常见的标准蓝图。软件规约同样呈现完整的光谱分布: 1. **形式化规约(Formal Specifications)**:使用严谨的形式化语言编写,主要针对极端复杂、精妙或攸关生死的关键系统(尤其是并发系统与分布式系统)。由于人类无法仅靠肉眼确保此类系统的正确性,必须借助自动化工具进行模型检验,而工具无法解析模糊的人类散文,因此形式化语言不可或缺。 2. **数学散文(Mathematical Prose)**:位于光谱居中位置,使用精确且带有数学严谨性的自然语言书写,适用于逻辑较为精妙、需要清晰推导但未达到全盘形式化程度的代码。 3. **简短散文规约(Short Prose Specifications)**:光谱的另一端,绝大多数普通代码逻辑非常简单,写下寥寥几句清晰的散文说明便已足够。 --- ### [06:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=400s) 像科学家一样建立数学模型:函数模型 如何写出合格的规约?Lamport 主张计算机专业人员应当像真正的计算机科学家——即自然科学家——那样去思考。科学思维之所以能取得辉煌成就,关键在于科学善于为客观现实建立“数学模型”(Mathematical Models)。例如天文学起步时,面对行星上拥有高山、海洋、潮汐与多变气候等无穷复杂现实,早期天文学家抽象出了一个极其成功的极简模型:将行星视为仅具有位置(Position)与动量(Momentum)的质点。对于计算机科学,其现实是芯片、游戏机或正在执行程序的物理计算机,而计算机科学家需要通过抽象模型来捕捉其核心行为。 Lamport 指出,计算机科学中有图灵机、偏序事件集等多种抽象模型,但其中最基础、最实用的两大支柱模型是:**函数(Functions)** 与 **状态序列(Sequences of States)**。 在谈及函数模型时,Lamport 用纯数学视角拆解了函数:函数在数学上极其纯粹,本质就是一个“有序对的集合”(A set of ordered pairs)。例如在自然数集 $\text{Nat}$ 上的平方函数 $\text{square}$,就是集合 $\{(0,0), (1,1), (2,4), \dots\}$。有序对 $(2, 4)$ 属于该集合,即通常所记的 $\text{square}(2) = 4$。定义一个函数只需两步: 1. 明确声明该函数的定义域(Domain)——例如 $\text{domain}(\text{square}) = \text{Nat}$; 2. 规定定义域内任意元素 $x$ 所映射的目标值——即映射到 $x^2$。 关于数学概念与编程语言的差异,Lamport 坦率而幽默地划清界限:“Functions in math are not the same as functions in programming languages. Math is much simpler. I'm not a programming language expert, so I don't know what those things that programming languages call functions are, but I know what math is, and math is a lot simpler.”(数学中的函数与编程语言中的函数不是一回事。数学要简单得多。我不是编程语言专家,所以我不知道编程语言里那些叫做函数的东西到底是什么,但我知道数学是什么,数学要简单得多。) --- ### [10:28](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=628s) 标准行为模型:状态机与欧几里得算法 尽管函数模型极其强大,但它存在显著的局限性:它只能刻画程序“做了什么”(输入映射到输出),却无法刻画程序“如何做到的”。例如,快速排序(Quicksort)和冒泡排序(Bubble Sort)在数学上计算的是完全相同的输入输出映射函数,但二者在算法与程序实现上天差地别。更重要的是,许多程序并不是简单的批处理转换器,而是需要长期运行甚至永不停止的(例如操作系统)。你无法将操作系统抽象为一个单步输入到输出的函数。 为此必须引入 Lamport 所称的**标准行为模型(Standard Behavioral Model)**: - **状态(State)**:变量到值的赋值分配(An assignment of values to variables)。 - **行为(Behavior)**:一个由状态构成的序列(A sequence of states)。 - **程序(Program)**:被建模为所有可能执行行为的集合(A set of behaviors)。 Lamport 以欧几里得辗转相减求最大公约数算法(Euclid's Algorithm)为例进行了具体演示。设输入变量为正整数 $m$ 与 $n$,算法初始化将 $x$ 赋为 $m$,$y$ 赋为 $n$,随后不断用较大数减去较小数,直至 $x = y$ 时终止,此时两数的值即为最大公约数。当 $m=12, n=18$ 时,算法的单一行为状态转移序列清晰展开: ```mermaid flowchart TD accTitle: Euclid's Algorithm State Transitions accDescr: State progression of Euclid's algorithm for inputs m=12 and n=18 ending in a state where x equals y equals 6. S1["初始状态 S1<br/>x = 12, y = 18"] -->|"y > x: y:= y - x (18 - 12)"| S2["状态 S2<br/>x = 12, y = 6"] S2 -->|"x > y: x:= x - y (12 - 6)"| S3["终止状态 S3<br/>x = 6, y = 6"] ``` 在状态 S3 下,由于 $x = y = 6$,循环判定条件终止,全过程仅产生这一条确定的状态序列行为。 --- ### [13:04](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=784s) 行为集合的分解:安全性与活性 在确立了“程序即行为的集合”这一模型后,Lamport 提出了一个核心理论问题:我们应当如何精准刻画和描述这样的行为集合? Lamport 指出,理论上存在一条极其关键的基础定理:**任何行为集合 $B$,均可精确表示为一个满足安全性(Safety)属性的行为集合与一个满足活性(Liveness)属性的行为集合的交集(Conjunct / Intersection)**。 为了便于直观理解,Lamport 给出了这两类属性的非形式化判别准则: - **安全性属性(Safety Property)**:如果该属性为假,当且仅当它可以在该行为执行的某一个离散时间点被判定违背。“部分正确性”(Partial Correctness)便是典型的安全性属性——若程序运行到某一步输出了错误答案并停止,在它给出错误答案的那个瞬间,你就能断定它破坏了正确性。简言之,安全性保证“坏事情绝不会发生”。 - **活性属性(Liveness Property)**:你无法在中间的任意有限前缀状态下判断该属性是否被违反,必须观察整条完整的无限行为序列才能判定其真伪。计算机科学中最经典的活性属性就是“程序终止性”(Termination)——在程序运行的任何一个有限时间段内,你都无法咬定它未来绝对不会停止,唯有纵观整个行为的全局生命周期,才能确证它是否真正终止。简言之,活性保证“好事情最终一定会发生”。 关于两者的划分与行为描述的论述,Lamport 直截了当地指出,任何复杂的软件系统行为,都可以通过严格剖析这两大属性来进行无死角的精确界定。 ### [14:18](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=858s) 规范安全性属性与数学描述 系统的行为集合是通过两类性质来界定的:安全性属性(Safety Property)和活性属性(Liveness Property)。如果要判断一个程序是否永远不终止,单看有限片段是不够的,必须观察其整个行为轨迹。不过在工程实践中,安全性属性的规范往往更重要,因为大多数 subtle(微小且隐蔽)的错误都藏在安全性中。为了节省时间,Leslie Lamport 选择忽略活性属性,聚焦于如何规范安全性。规范安全性只需两样东西:一组所有可能的“初始状态集合”(Set of Initial States),以及一个描述任意状态之所有可能后续状态的“下一状态关系”(Next-state Relation)。 当探讨该用何种语言来书写这些规范时,Lamport 主张像科学家一样思考——科学的通用语言就是数学。以欧几里得算法为例,初始状态是由一个数学谓词公式定义的。如果设常量为 $M$ 和 $N$,那么初始公式就是 $x = M \land y = N$;在给定的输入下,只有唯一的一对状态值满足该公式。 ```mermaid flowchart LR accTitle: 安全性规范的核心构成 accDescr: 展示安全性属性由初始状态集合与下一状态关系共同构成的逻辑流 subgraph Spec["安全性规范 (Safety Specification)"] Init["初始状态公式<br>(Initial States)"] Next["下一状态关系公式<br>(Next-state Relation)"] end Init -->|"确定起点"| States["状态转移轨迹 (Behaviors)"] Next -->|"约束每一步跳变"| States ``` - 安全性属性表明“坏事永远不会发生”,活性属性表明“好事最终一定会发生”; - Lamport 指出:“Now in practice specifying safety just turns out to be more important because that's where errors are most likely to occur and tend to be more subtle.” - 状态转换的规范使用未加撇的变量(如 $x, y$)表示当前状态,使用带撇变量(如 $x', y'$)表示下一状态。 --- ### [16:09](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=969s) 欧几里得算法的状态转移与非确定性建模 欧几里得算法的下一状态关系本质上是一个析取公式(逻辑“或”)。算法执行时有两种情况:若 $x > y$,则 $x' = x - y \land y' = y$;若 $y > x$,则 $y' = y - x \land x' = x$。整个转移公式即为这两个条件的析取(Disjunction)。 Lamport 用具体数值演示了状态推进的整个推导过程:假设取 $M = 12, N = 18$。初始状态唯一的解是 $x = 12, y = 18$。代入下一状态公式时,$12 > 18$ 为假,逻辑与运算导致第一分支为假;而 $18 > 12$ 为真,计算得出下一状态中 $y' = 18 - 12 = 6$,且 $x' = 12$。在随后的推导中,第三个状态得出 $x = 6, y = 6$。当试图在 $x=6, y=6$ 的状态下寻找下一步时,由于两个分支的条件($6 > 6$)均为假,整个公式求值结果为假,不存在任何 $x', y'$ 能够满足公式——这意味着系统没有后续状态,程序正式终止停机。 由于对任意一组特定的 $x, y$,满足下一状态公式的 $x', y'$ 要么只有唯一一组解,要么无解,因此欧几里得算法是确定性的(Deterministic)。Lamport 进而破除对“非确定性”(Non-determinism)的神秘化认知:所谓非确定性,在数学形式化里没有任何玄妙之处,它无非是在单一一组当前未加撇变量赋值下,存在多组能够使公式为真的带撇变量赋值而已。 - 初始状态:$x = 12, y = 18$; - 第二步状态:$x = 12, y = 6$; - 第三步状态:$x = 6, y = 6$; - 第四步尝试:$6 > 6$ 为假,无继发状态,程序自然终止。 --- ### [20:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1240s) 形式化规范工具与 TLA+ 的现实威力 数学本身可以写得非常非正式,但如果工程师想要借助自动化工具分析系统,就必须采用形式化语言。Lamport 引入了他发明的 TLA+。在 TLA+ 中,欧几里得算法的数学公式被包装进一个模块(Module)中,声明常量 $M, N$ 与变量 $x, y$,并扩展标准整数模块(Integers)以获得加减法和比较运算支持。 有了形式化规范,便能运行模型检验(Model Checking)。模型检验会在一个小规模模型上穷举检查程序的所有可能行为,这种方法极其实用且易于操作——只需指定常量实例化参数,模型检验器就能在几纳秒内完成对类似欧几里得算法的遍历。此外,开发者也可以用 TLA+ 编写机械证明,并由定理证明器自动核验,不过证明的工作量相当大。 针对很多工程师认为“数学只适用于玩具示例(Toy examples),真实世界系统必须依靠包含类型、过程、对象的现实编程语言”的偏见,Lamport 斩钉截铁地斥责为“Wrong”(完全错误)。 ```mermaid flowchart TD accTitle: TLA+ 在工程实践中的落地链路 accDescr: 展示从书写规范到模型检验,再到在工业界中发现核心漏洞的应用过程 TLA["TLA+ 规范 (数学逻辑)"] --> MC["模型检验器 (TLC) / 定理证明器"] MC --> Val["小模型遍历验证"] Val --> Amazon["Amazon 分布式系统<br>(性能大幅优化且保真)"] Val --> Xbox["Xbox 360 内存系统<br>(排查出导致4小时必死机的严重漏洞)"] ``` 为了反驳形式化方法“会牺牲系统性能”以及“管理层绝不允许工程师花时间做这个”的陈词滥调,Lamport 引用了亚马逊(Amazon)工程师 Chris Newcomb 的实战评价。Newcomb 指出,亚马逊在 10 个大型复杂真实系统上应用了 TLA+,每次都带来了显著价值:不仅在投产前拦截了微小且致命的严重 Bug,更关键的是让团队获得了足够的底层洞察力与信心,敢于进行激进的性能优化而不必担心破坏正确性。亚马逊的管理层甚至在年度规划中主动为团队划拨工程时间来编写 TLA+ 规范。 Lamport 还分享了一个经典案例:图灵奖得主 Chuck Thacker 让一名实习生用 TLA+ 为 Xbox 360 的内存系统编写规范。甚至还没到运行模型检验器的那一步,仅仅是在撰写规范整理思路的过程中,就发现了一个极其严重的硬件架构 Bug。IBM 的内存系统设计师们一开始抓耳挠腮,仔细核对后最终承认那确实是一个致命缺陷。这个隐蔽漏洞无法通过普通的测试流程被捕获,一旦硬件流片并上市销售,它将导致全世界所有的 Xbox 360 在开机运行 4 个小时后全部崩溃。 - Chris Newcomb 评价道:“We have used TLA+ on 10 large complex real-world systems. In every case, TLA+ has added significant value, either preventing subtle serious bugs from reaching production, or giving us enough understanding and confidence to make aggressive performance optimizations without sacrificing correctness.” - 业界关于管理层阻挠的顾虑与事实相反,亚马逊管理层在年度预算和排期中主动支持 TLA+ 规范编写; - 编写 Xbox 360 规范阶段拦截的硬件 Bug,足以让全球主机在连续运行四小时后集体崩溃。 --- ### [25:13](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1513s) 非形式化规范的必要性:排版工具 TLATeX 的启示 虽然 TLA+ 效果显著,但 Lamport 明确表示本场演讲的重心并不是推销 TLA+,而是讨论“非形式化规范”(Informal Specifications)。他以自己编写的 TLA+ 源码美化排版工具 TLATeX 为例展开说明。 用户在输入代码时可能会写出非结构化的 ASCII 符号。最原始的排版做法是机械地将 ASCII 字符逐字翻译为数学符号(例如把 `/\` 转换为合取符号 $\land$)。但 TLA+ 是一门格式敏感的语言,对齐在语法上具有实际意义:用户输入的公式中,合取符号通常要求必须严格垂直对齐,否则可能会改变整个公式的逻辑含义;同时等号也往往需要对齐。而在另一种输入场景下,用户可能只是随手敲了两个碰巧处于同一列的符号,若程序死板地将其强行对齐,反而违背了作者的原意。 这带来了一个棘手的现实:根本不存在一套关于“何为正确对齐”的精确形式化定义。“正确性”完全取决于用户心里的主观期望,而用户的内在心理预期是无法被数学公式彻底形式化的。如果连正确性本身都无法形式化,规范还有用吗?Lamport 给出了深刻的反转:不知道程序具体应该做什么,绝不意味着你可以不用思考、直接跳去写代码;恰恰相反,这说明你需要更深入地思考。 - 机械排版无法分辨哪些对齐是有意的语法结构,哪些只是字符输入中的偶然重合; - 用户的主观意图无法通过数学逻辑进行完全的形式化表达; - Lamport 总结道:“Not knowing what a program should do doesn't mean you don't have to think, you just do this code. It means you have to think a lot harder, which means that that's what a spec is even more important when you don't know what the program is supposed to do.” ### [27:19](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1639s) 代码格式化工具的非形式化规范:六条数学规则 在处理代码格式化工具(Pretty Printer)时,最难的往往不是编写代码,而是界定它到底应该做什么。因为工具必须产生确切的排版输出,而决定“怎样排版才是对的”需要大量的思考,而思考必须落实到文字上。Leslie Lamport 解释了他当时的应对做法:他没有直接动笔写实现代码,而是写了一份由六条规则以及若干定义构成的规范。这些规则以写在注释里的“非形式化数学散文”形式呈现。 他当场向观众展示了其中一条规则的原文:“左注释标记(left comment token)与其覆盖标记(covering token)保持左注释对齐。”Lamport 幽默地坦白说,时至今日他自己若不重新阅读规范,也记不清这到底确切指什么了,但“左注释对齐”和“覆盖标记”都是规范里被严格定义的术语。这本质上就是数学散文。为什么非要写这份规范?因为理解并调试这 6 条规则,远比调试 850 行代码要容易得多——而且如果不先写出规则,最终的代码规模很可能远超 850 行。 他在规则层面进行了大量调试工作。写出一堆规则后,它们组合起来会产生何种效果往往并不直观,因此他编写了专门的调试代码来观察每个样例触发了哪些规则。一旦格式化工具输出了奇怪的结果,他就能立刻定位并指出:“哦,针对这种情况需要修改这条规则。”这完全是一个渐进迭代的过程。由于规范想得清清楚楚,后续将规则落实为代码时几乎没出什么 Bug,即便有几个小问题也极其容易排查。正如 Lamport 所断言的:“Had I just written the code, it would have taken me much longer to just get something I could live with and I'm sure the results wouldn't have produced formatting nearly as good.” 至于当时为什么没有写成一份形式化的 TLA+ 规范,Lamport 给出了三点原因: - 排版格式哪怕有一两处没完全对齐,世界也不会因此崩溃; - 代码格式化工具本就无法兼顾所有罕见的极端边界情况(corner cases); - 当时没有任何形式化验证工具能派上用场——模型检验器(Model Checker)必须有明确要验证的性质,而 TLA+ 工具并不是为代码格式化排版这类问题设计的。 --- ### [30:01](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1801s) 规范的共性与特异性:跳脱于代码之外的高维思考 回顾这份格式化工具规范,Lamport 分析了它具有代表性(典型)和非典型的一面。这份规范典型的地方在于,它处在比代码更高的抽象层次上,能够用任何编程语言实现。人们所钟爱的任何编程方法论或开发工具在此都派不上丝毫用场,因为没有任何一种“编写更好代码的方法”能够替代撰写规范的必要性。正如 Lamport 强调指出的:“You write a spec to help you think about the problem before you think about the code.” 而非典型的地方则在于它相当微妙。绝大多数程序员日常编写的 95% 的代码并不需要如此深奥的思考,通常一份更简短、更直接的规范便已足够。此外,该规范采用了一组规则(Rules)的形式,而纯粹罗列规则、需求或公理通常是很糟糕的规范风格,因为多条规则交织在一起往往难以让人直截了当地理解系统全貌。然而在这个具体的排版问题中,规则集恰恰是最合适的契合形态,这也表明并不存在一套放之四海而皆准的万能规范写法。 ```mermaid flowchart TD accTitle: Specification vs Implementation Complexity accDescr: 对比格式化工具与排序算法在规范制定与代码实现两个维度上的复杂度差异 subgraph PrettyPrinter["代码格式化工具 (Pretty Printer)"] PP_Spec["规范极难制定<br/>(需界定何为良好排版)"] --> PP_Impl["代码实现相对容易<br/>(按已定规则编写即可)"] end subgraph QuickSort["快速排序算法 (Quicksort)"] QS_Spec["规范极其简单<br/>(保证数组有序排列)"] --> QS_Impl["高效实现极具挑战<br/>(设计精妙算法与分区)"] end ``` 这种对比引出了一个深刻的洞见: - **指定函数行为的难易程度因问题而异**:为格式化工具指定“应该输出什么”极难,但根据想好的规则去实现它却很容易; - **实现算法的难易程度同样因问题而异**:为排序程序指定“应该做什么”非常简单(输出必须是原输入的有序排列),但若没人教过你,从零想出一个高效的实现却困难重重。无论哪种情况,深入的思考都必不可少,而这都要求写下规范。 --- ### [32:03](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1923s) 快速排序的伪代码规范及其隐藏的局限 为了展示如何为复杂实现编写规范,Lamport 以 Tony Hoare 发明的快速排序(Quicksort)为例展开分析。快速排序是一种分治算法,用于对数组 $A[0 \dots n-1]$ 进行排序(为简单起见假定为数字数组)。它依赖一个 `partition`(分区)过程,接受参数 `low` 和 `high`,其作用是在范围 `low` 到 `high-1` 之间选取一个基准点 `pivot`,并对这部分数组进行重排,使得基准点左侧(`low` 到 `pivot`)的所有元素都小于等于右侧(`pivot+1` 到 `high`)的所有元素。 如果用常见的伪代码来书写快速排序的规范,通常会写成一个递归函数 `qs(low, high)`: 1. 若 `low < high`,则在区间 `[low, high]` 上调用 `partition` 过程,取得返回的 `pivot`; 2. 随后在划分出的两个子区间上递归调用 `qs(low, pivot)` 和 `qs(pivot + 1, high)`; 3. 若 `low` 不小于 `high`,说明区间内最多只有一个元素,无需排序直接返回; 4. 主程序只需针对整个数组调用 `qs(0, n - 1)`。 这种没有形式化语法约束、没有冗余类型声明的伪代码,本身就是一份极好的规范,它通俗易懂,无论转换成哪种编程语言去编码都轻而易举。但 Lamport 紧接着抛出了一个尖锐的质问:这真的是快速排序的本质吗?这不过是人们描述快速排序最常见的方式罢了。递归并不是快速排序的本质特征,它仅仅是实现“分治思想”的手段之一;而且若想在现代多核处理器上并行运行快速排序,递归实现很可能并不是最佳方案。 --- ### [34:32](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2072s) 思维被代码困住:挑战十分钟写出非递归快排 Lamport 向全场观众抛出了一个他经常用来考朋友和同行的问题:“写一个非递归版本的快速排序。”他指出,几乎没有程序员能够在 10 分钟内站在黑板前把它正确写出来。绝大多数人一拿到题目,下意识的反应都是在脑海中试图“编译”并展开那个递归程序(模拟调用栈),而这几乎必然会在现场手写时陷入混乱并漏洞百出。 随后,Lamport 揭示了跳出代码层面的真正解法:本质上,我们只需要维护一个集合 $U$,其中存放着所有“尚待调用 partition 进行划分的索引区间”。 - **初始状态**:$U$ 仅包含一个区间元素,即整个数组的起止范围 `[0, n-1]`; - **后续步骤**:每次从 $U$ 中任意取出一个待处理的区间,调用分区操作,将其分裂为两个更小的子区间,并将这两个新区间放回 $U$ 中,直到集合 $U$ 变为空集为止。 用这种集合的观点来看,问题立刻变得清晰明朗,而且它自然而然就是并发友好的——多个计算核心完全可以并发地从集合 $U$ 中取出不同区间进行处理。 --- ### [35:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2140s) 数学视角的非递归快排:状态转换关系与泛化 既然理清了核心概念,Lamport 提倡“像科学家一样思考”,直接用数学中的初始谓词 $Init$ 和下一状态关系 $Next$ 来精准描述该算法: ```mermaid flowchart TD accTitle: Iterative Quicksort State Transition accDescr: 基于未排序区间集合U与状态转换关系的非递归快速排序执行流程 InitState["初始状态 Init<br/>A = 任意长为 n 的数字数组<br/>U = {[0, n-1]}"] --> CheckEmpty{"U 是否为空集?"} CheckEmpty --"是 (终止)"--> Done["算法结束,A 完全有序"] CheckEmpty --"否 (执行 Next 步骤)"--> PickRange["从 U 中任选一个区间 [B, T]"] PickRange --> CheckSingle{"B 是否等于 T?"} CheckSingle --"是 (单元素区间)"--> RemoveSingle["A' = A<br/>U' = U \ {[B, T]}"] CheckSingle --"否 (多元素区间)"--> DoPartition["选取基准点 P ∈ [B, T-1]<br/>重排 A 得到符合分区的 A'<br/>U' = (U \ {[B, T]}) ∪ {[B, P], [P+1, T]}"] RemoveSingle --> CheckEmpty DoPartition --> CheckEmpty ``` 1. **初始谓词 $Init$**: - 数组 $A$ 初始化为长度为 $n$ 的任意数字序列; - 集合 $U$ 初始定义为仅包含初始区间对的集合:$U = \{ (0, n - 1) \}$。 2. **辅助定义**: - 定义 $\text{partitions}(B, P, \text{low}, \text{high})$ 为对数组 $B$ 在指定范围内按基准点 $P$ 完成分区重排后,所允许产生的所有可能的新数组的集合。 3. **下一状态关系 $Next$**(描述变量旧值 $A, U$ 与新值 $A', U'$ 之间的关系): - 如果 $U = \emptyset$,状态转换终止; - 若 $U \neq \emptyset$,非确定性地任选一对区间 $(B, T) \in U$; - 若 $B \neq T$:在 $B$ 到 $T-1$ 之间任选一个基准点 $P$,令新数组 $A'$ 为合法分区结果集中的任意一个,新集合 $U'$ 则从 $U$ 中移除已处理的 $(B, T)$,并加入划分出的两个新子区间 $(B, P)$ 与 $(P+1, T)$; - 若 $B = T$:说明该区间只有一个元素无需再排,保持 $A' = A$,新集合 $U'$ 仅需把 $(B, T)$ 从 $U$ 中剔除。 Lamport 指出,这个用 $Next$ 关系定义的迭代式算法实际上比传统的递归版本**更为通用(More General)**。因为从系统行为(Behaviors)的角度来看,递归版本所产生的所有可能的数组演变序列,仅仅是这个数学规范所允许的行为轨迹的一个真子集。 面对“为什么几乎没人能想出这个快排版本”的疑问,Lamport 给出了切中要害的评价:“Because people's minds are stuck in code. They haven't learned to think at a higher level than the code.” --- ### [39:23](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2363s) 从数学表达到 TLA+ 与 PlusCal 将上述直白描述转化为严谨的数学公式其实轻而易举:“任意选取”在逻辑学中就是存在量词 $\exists$(只要存在 $(B, T) \in U$ 即为真);而“令 $A'$ 为集合中的任意元素”就是集合成员关系 $A' \in \dots$。将这些逻辑关系和集合运算组合起来,得到的就是一个完备的形式化 TLA+ 规范。 对于那些更习惯阅读伪代码而非纯数学逻辑符号的工程师,Lamport 介绍了 PlusCal 语言。PlusCal 表面看起来像是一种极简的“玩具编程语言”,其算法可以直接嵌入在 TLA+ 模块的注释当中。但它最独特、最强大的地方在于:它的语法结构支持非确定性表达,并且代码中的任何表达式都可以直接书写为任意合法的 TLA+ 数学表达式。这赋予了 PlusCal 极其惊人的表达能力,使其远比任何传统编程语言更具表现力和抽象思维能力。 ### [40:51](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2451s) PlusCal、并发系统与模型检测的必要性 Leslie Lamport 继续探讨形式化规约工具的实际应用。他提到,PlusCal 的表达能力超越了任何编程语言设计者的梦想,并且它可以直接编译成易于理解的 TLA+ 规约。Lamport 透露,自己在日常工作中就经常先用 PlusCal 编写算法,而在证明系统正确性时,则直接针对编译生成的 TLA+ 规约进行推理,并利用 TLA+ 工具链(包括模型检测器 TLC 和定理证明器 TLAPS)来验证翻译后的逻辑。 讨论的重心随即转向“永远运行的程序”(programs that run forever),与此前讨论的计算单次函数的简单程序不同,这类程序通常涉及并发、操作系统以及分布式系统。Lamport 坦诚直言:“很少有人光靠思考和写作就能把并发程序搞对,我自己就不是其中之一;我们需要工具来检查我们的工作。”(“Few people can get them right by just thinking and writing. I'm not one of them. We need tools to check what we're doing.”)这也正是亚马逊工程师认为 TLA+ 极其有用的根源——在于其强大的模型检测能力。对于分布式和并发算法,单靠脑力根本无法保证正确性,必须撰写规约并通过工具进行模型检测。 针对系统构建中剩余 95% 相对简单的常规代码,Lamport 展示了自己曾在一个实际程序中撰写的一小段规约示例。他自问为何要写这段看似简单的规约:答案是为了在真正动手编码前,确保自己准确清楚代码应该做什么。没有规约时,人往往只是“自以为知道”代码该做什么,只有写下规约,才能验证它是否真的像想象中那样简单明了;而且日后回顾时,根本无需重新阅读代码实现,只需看一眼规约就能明白其功能。 ```mermaid flowchart TD accTitle: PlusCal and TLA+ Verification Workflow accDescr: PlusCal algorithm compiles to TLA+ spec and gets verified with tools or mapped to implementation. subgraph SpecPhase ["规约与验证阶段"] A["PlusCal 算法描述"] --> B["编译生成 TLA+ 规约"] B --> C["TLA+ 工具链 (TLC 模型检测器 / 定理证明器)"] C --> D{"验证是否正确?"} D -- "发现错误" --> A end subgraph CodePhase ["实现与代码阶段"] D -- "验证通过" --> E["编写代码实现"] B -. "规约作为注释说明设计".-> E end ``` ### [43:26](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2606s) 思考即书写:规约的定义与核心层级 Lamport 针对程序员乃至所有思考者提出了一条核心原则:人人都以为自己在思考,但如果不把想法写下来,其实只是在自欺欺人。“程序员关于思考最应该明白的一点,就是在编码前先思考;这意味着你应当在编码前先动笔写,而规约不过就是你在编码前写下的东西而已。”(“What programmers should know about thinking is that you should think before you code, which means you should write before you code. A spec is simply what you write before coding.”)规约的对象应当覆盖任何他人可能使用或修改的代码——而这个“他人”很可能就是一个月后早已忘光细节的你自己;它既可以是一个完整系统、程序,也可以是一个类、一个方法,甚至是方法内部某段棘手的逻辑。 关于规约应当描述的内容,Lamport 明确划分为两个维度: 1. **做什么(What it does)**:必须包含任何使用者使用该代码所需要知道的全部信息。 2. **怎么做(How it does it)**:如果内部实现复杂、精妙,需要深度思考才能做对,就必须规约其高层设计与算法。 在思考或规约代码时,必须站在“代码之上的抽象层”(above the code level): - 采用数学方式进行思考,用状态(states)和行为(behaviors)来描述并发或反应式系统,用输入输出关系(functions)来描述功能计算。 - 无论具体书写是极其形式化还是非形式化的伪代码,思维底色必须是数学化的。 - 要学会写非形式化规约,最有效的方式恰恰是先学习写形式化规约(比如通过编写 TLA+ 规约并在模型检测器中运行排错),正如通过写代码、运行并修正错误来学会编程一样。 - 规约与代码之间的桥梁通常通过注释建立,在注释中解释数学抽象概念与底层数据结构之间的映射(例如数学上的图与代码中的节点对象数组、边对象数组之间的对应关系)。 ### [47:07](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2827s) 为什么程序员抗拒规约?代码修改与“补丁”恶性循环 谈到具体的编码环节,Lamport 澄清自己无意指导程序员该用哪种语言、采用何种编码方法论或怎样调试;规约并不能直接消除手误类的编码低级 Bug,它的核心价值在于在源头捕获算法与设计层面的结构性错误。写规约确实是一项额外的步骤,但它通过及早发现错误来节省总体时间,并从根本上提升编程质量。 为什么程序员普遍抗拒写规约?Lamport 引用了古希腊名言(他打趣地记成阿基米德,实为欧几里得对托勒密国王所言:“几何无王者之道”),指出“写作是困难的,因为思考是困难的”,而欺骗自己“正在思考”却轻而易举。写作就像跑步,必须经过持续刻苦的练习来锻炼“写作肌肉”。人们总能找到借口推脱,其中最常见的借口莫过于:“万一规约写错了怎么办?需求变了、需要功能增强了怎么办?” Lamport 坦承,所有有用的程序最终都会面临修改,现实中几乎没有人会在需求变动时推倒重来编写一份完美的全新规约,大多是对代码打补丁(patch),顺便更新规约。但这绝不是不写规约的理由,原因有二: - **修改者的福祉**:后续负责修改代码的人(往往就是未来的作者本人),会为前人写下的每一个规约词汇或数学公式心存感激。 - **防止系统从第一天起就沦为补丁**:代码每打一次补丁就会变得更丑陋、更难懂、更难以维护;“如果你不从规约开始,你写下的每一行代码从一开始就只是一个补丁。”(“If you don't start with a spec, every piece of code you write is a patch.”) Lamport 引用德怀特·艾森豪威尔的名言做结:“没有哪场战役是完全按照计划打赢的,但没有哪场战役能在没有计划的情况下打赢。”(“No battle was ever won according to plan, but no battle was ever won without one.”)当有人告诉你写规约是浪费时间时,他们本质上是在叫你不要思考;思考不能保证你不犯错,但放弃思考通常能保证你必定犯错。 - 演讲结束时听众报以热烈掌声,Lamport 引出提问环节,并看着台下打趣道:“我想我们还有时间回答几个问题……非常少的问题,大概一个。希望这(人数少)不是对我演讲质量的评判。” ### [52:35](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3155s) 问答环节:按示例规约(Specification by Example)的陷阱 在问答环节中,一位现场听众提问:“感谢您的演讲。请问您如何看待‘按示例规约’(Specification by Example)?这是一种受测试驱动开发(TDD)启发的实用主义(pragmatic)方法。虽然它不完整,但鉴于形式化完整性本身无论如何也是不可判定的,这算不算一种好的实用主义手段?” Lamport 毫不客气地打断并直接纠正对方的措辞:“不,这是一种极其糟糕的实用主义方法(terrible pragmatic approach)。” Lamport 进一步阐述了他激烈反对的理由: - **解释 vs 规约**:用具体示例向他人解释概念是极好的手段,但在设计阶段,示例起到的完全是反作用。 - **思维局限**:示例会阻碍你的思考,把你局限在某几个具体的用例中。 - **编程的核心难点**:编程的真正挑战绝不是应付那些浅显、简单、显而易见的美好场景(nice cases),而是在于周密地处理那些隐蔽而复杂的极端边界情况(hard cases)。按示例规约恰恰让人忽视了最致命的复杂情形。 ### [53:19](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3199s) 依样画葫芦的陷阱:示例无法替代规范 Leslie Lamport 在谈到系统抽象与文档时,严厉批评了软件工程界流行的一种不良倾向——用“代码示例”来替代清晰的系统规范。他结合自己在开发 Eclipse 插件时的亲身经历现身说法:Eclipse 平台中有无数的方法与 API,它们的文档往往只给出一个“演示如何调用该方法”的代码片段。然而,一旦开发者试图脱离那个极其特定的演示场景去实现自己的业务需求,代码就会彻底失效,并且让人根本搞不清楚为何失败。Lamport 斩钉截铁地总结道:“以示例来编程我不知道行不行得通,但以示例来充当规范是绝对行不通的(programming by example I don't know about you know if that works but a specification by example it doesn't)。” ### [54:09](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3249s) 问答:打补丁后的规范演进与版本保留 在随后的现场问答环节中,一位听众提问:当代码修补(Patch)或修复缺陷之后,开发者应当如何处理旧版本的形式化规范?是直接丢弃,还是保留旧规范以维护历史上下文? Lamport 回答称,每当修复 Bug 导致必须修改规范时,他一定会详细记录下修改规范的原因,因为如果不做记录,“两年后重新回看时,我就会困惑自己当初为什么要做那次修改”。关于这些历史记录对规范文本本身能发挥多大效用,他持开放态度,但他坚信在存储成本几乎为零的今天,丢弃任何信息都是不明智的。他明确指出:“在当今时代,我们不应该扔掉任何信息,保存它们没有任何成本,所以你当然应该把旧规范保存下来(we shouldn't be throwing you know information away these days you know it doesn't cost anything to save it so you should certainly save the old specs)。”至于新规范文本内部是否需要详细列出每次改动的变迁历史,他表示自己并没有一个绝对的标准答案。 ### [55:17](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3317s) 问答:当今工业界的拿来主义与 5% 的致命代码 一位拥有形式化方法博士背景的听众提出了一个尖锐的现实问题:在当今软件工业界,绝大多数程序员早已不再编写形式化规范;现代软件构建的常态(高达 99% 的比例)是从现有的开源项目或算法中直接复制、拼接并调整代码,以拼凑出所需功能。 Lamport 对此做出了深刻的回应。他首先反驳了“拼凑法能做出好软件”的假象,指出这种开发模式之所以让人极其痛苦,正是因为其他人编写的模块缺乏形式化规范,导致开发者在集成第三方代码时,必须耗费大量无谓的时间去逆向推导一段代码到底在做什么。Lamport 坦承,软件工程中确实有 95% 的代码属于常规操作,几乎不需要繁琐的规范,只需寥寥几行自然语言即可阐明;但剩下的 5% 却暗藏玄机,充满了精妙而复杂的底层逻辑。他发出严厉警告:“如果你在处理那极具难度的 5% 时,依然沿用处理另外 95% 代码时那种不动脑子的做法,你一定会搞砸得一塌糊涂(because we use the same techniques that you use you're the same lack of thought that you use when you're doing that other 95% you're gonna screw up badly)。” ```mermaid flowchart TD accTitle: 软件工程中的代码二八定律与思考深度 accDescr: 展示常规粘合代码与复杂核心代码在规范需求和思维方式上的巨大差异 subgraph Routine["95% 的常规拼凑代码"] A["胶水代码 / 简单组装 / 库调用"] B["极低规范需求<br>(寥寥数行或常规注释即可)"] A --> B end subgraph Critical["5% 的精妙核心代码"] C["高并发 / 分布式 / 复杂状态交互"] D["极高思考密度与严格形式化规范需求"] C --> D end Routine -.->|"若将缺乏思考的拼凑习惯<br/>套用于核心代码"| BadResult["系统出现隐蔽死锁或严重溃败"] Critical ==>|"严谨思考与形式化建模"| Reliable["可靠、正确的复杂系统"] ``` ### [57:12](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=3432s) 问答:时序逻辑是“必要之恶”与 TLA+ 的设计哲学 最后一位提问者指出,在学习过时序逻辑(Temporal Logic)后,不难看出 Lamport 所讲授的理论底层充斥着时序逻辑的影子。他询问 Lamport 是否考虑过在系统描述中更直接、更广泛地使用时序逻辑的原生语言,例如大量引入“未来算子(Future Operator)”和“必然性算子(Necessity Operator)”等逻辑符号。 Lamport 给出了一个语惊四座的论断:“时序逻辑是邪恶的(temporal logic is evil)。”他随即解释说,时序逻辑之所以被视为“恶”,是因为它极其晦涩反直觉、极其难以正确使用;然而,“它最终被证明是一种必要之恶(it turns out it's a necessary evil)”,因为在形式化刻画系统的活性(Liveness,即系统终将发生期望的行为)时,时序逻辑是人类目前能找到的最佳思维工具。 不过,Lamport 进一步强调,在工程实践中“安全性(Safety,即坏事永不发生)远比活性更重要”。处理安全性问题时,开发者实际上完全不需要懂得任何深奥的时序逻辑。这也是 TLA+(Temporal Logic of Actions)相比其他时序逻辑流派卓越得多的原因: ```mermaid flowchart LR accTitle: TLA+ 规范的组成结构 accDescr: 展示千行 TLA+ 规范中普通数学公式与最终时序逻辑公式的比例划分 subgraph TLASpec["典型的一千行 TLA+ 规范架构"] direction TB M["999 行:基础离散数学与谓词逻辑<br>• 初始状态谓词(Init)<br>• 下一状态转换关系(Next)"] T["第 1000 行:单一时序逻辑公式<br>• 封装整体动态转移与活性要求"] M --> T end ``` - **99% 的内容回归普通数学**:在一个长达 1000 行的 TLA+ 规范中,前 999 行全部是基础的离散数学谓词——包括描述系统初态的 `Init` 以及描述状态转移关系的 `Next`; - **时序逻辑的极简收尾**:仅仅在全篇的最后一行,TLA+ 才通过一个简洁的时序逻辑公式将它们串联封装在一起。这使得普通开发者在不必精通深奥逻辑算子的前提下,依然能享受形式化方法带来的坚固保障。 问答结束后,研讨会主持人宣布全体大会(Plenary Session)正式闭幕。主持人向参会者交代了接送晚宴的大巴停靠位置、行李寄存安排以及当晚返回酒店的后勤路线,全场随后爆发出热烈的掌声向 Leslie Lamport 致谢。 ## Notable quotes > [47:07](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2827s) If you don't start with a spec, every piece of code you write is a patch. — Leslie Lamport > [30:01](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=1801s) You write a spec to help you think about the problem before you think about the code. — Leslie Lamport > [35:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=2140s) Because people's minds are stuck in code. They haven't learned to think at a higher level than the code. — Leslie Lamport > [14:18](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=858s) Now in practice specifying safety just turns out to be more important because that's where errors are most likely to occur and tend to be more subtle. — Leslie Lamport > [06:40](https://www.youtube.com/watch?v=-4Yp3j_jk8Q&t=400s) Functions in math are not the same as functions in programming languages. Math is much simpler. I'm not a programming language expert, so I don't know what those things that programming languages call functions are, but I know what math is, and math is a lot simpler. — Leslie Lamport ## Mentioned Leslie Lamport, Judith Bishop, Microsoft Research, Microsoft Research Silicon Valley Lab, MIT, Brandeis University, DEC, Compaq, SRI, LaTeX, TLA+, PlusCal, TLC, TLAPS, TLATeX, Richard Guindon, Tony Hoare, Chuck Thacker, Chris Newcomb, Dwight D. Eisenhower, Amazon, Xbox 360, IBM, Eclipse ## Video description Architects draw detailed blueprints before a brick is laid or a nail is hammered. Programmers and software engineers seldom do. A blueprint for software is called a specification. The need for extremely rigorous specifications before coding complex or critical systems should be obvious—especially for concurrent and distributed systems. This talk explains why some sort of specification should be written for any software. http://www.microsoftfacultysummit.com [Source](https://www.youtube.com/watch?v=-4Yp3j_jk8Q)

Source quotes

[47:07] Leslie Lamport: If you don't start with a spec, every piece of code you write is a patch. [30:01] Leslie Lamport: You write a spec to help you think about the problem before you think about the code. [35:40] Leslie Lamport: Because people's minds are stuck in code. They haven't learned to think at a higher level than the code. [14:18] Leslie Lamport: Now in practice specifying safety just turns out to be more important because that's where errors are most likely to occur and tend to be more subtle. [6:40] Leslie Lamport: Functions in math are not the same as functions in programming languages. Math is much simpler. I'm not a programming language expert, so I don't know what those things that programming languages call functions are, but I know what math is, and math is a lot simpler.

Source quotes

[48:03] Leslie Lamport: Every time code is patched, it becomes a little uglier, harder to understand, and harder to maintain; and if you don't start with a spec, every piece of code you write is a patch. [48:03] Leslie Lamport: Thinking doesn't guarantee that you won't make mistakes, but not thinking generally guarantees that you will. [2:10] Leslie Lamport: If you're thinking without writing, chances are you're fooling yourself, we're only pretending to think. [27:19] Leslie Lamport: You write a spec to help you think about the problem before you think about the code. [44:50] Leslie Lamport: Writing a spec isn't going to catch coding errors because it's not about coding; it'll catch algorithm errors. [4:10] Leslie Lamport: If you're going to use tools, it means you need a formal language, because tools don't understand prose. [32:03] Leslie Lamport: Recursion is not a fundamental part of quicksort; it's just one way of implementing divide and conquer.

Original transcript

[0:01] ladies and gentlemen please welcome [0:03] judith bishop [0:13] good afternoon everybody welcome back to [0:17] the plenary session of the Microsoft [0:19] faculty summit it's my great pleasure to [0:22] introduce to you today Leslie Lamport [0:24] who's a principal researcher at our [0:27] Microsoft Research Silicon Valley lab [0:30] so students and faculty who have taught [0:34] operating systems courses as well as [0:37] anybody who's really used a computer [0:39] will have benefited from Leslie's work [0:42] on logical clocks safety and liveness [0:45] sequential consistency and many other [0:49] parts of operating systems which laid [0:52] the basis for the safe operation of the [0:56] computers that we have today as they [0:58] moved from the single desktop to the [1:02] large number of interconnected devices [1:03] we have [1:06] Leslie before he joined Microsoft was a [1:09] member of certain various [1:12] prestigious institutions he was at MIT [1:15] at Brandis University and at companies [1:18] such as Dec Compaq and SR I [1:23] in his career he has also received [1:27] several honorary doctorates and he has [1:32] tried very hard to help scientists see [1:37] their workers beautiful and for that [1:40] purpose he also developed something that [1:42] I think you all know very well which is [1:44] let ik [1:45] he's now going to go on and try and [1:49] persuade engineers that there's [1:51] something else that is beautiful in life [1:53] which is mathematics and that's what [1:56] he's going to talk to us about today [1:58] so ladies and gentlemen please put your [2:00] hands together for this year's am Turing [2:03] Award winner Leslie Lamport [2:10] Thank You Judith [2:14] thinking above the code why do we think [2:19] well it helps us do most things [2:22] like hunting a saber-toothed tiger or [2:25] building a house or writing a program [2:29] so when should we think [2:32] well hunting a saber-toothed tiger we [2:37] should think before leaving the cave [2:39] when the tiger is charging at you it's a [2:43] little late [2:44] building a house [2:46] before beginning construction you don't [2:49] want to think about what you're doing [2:50] when the Carpenters are putting the roof [2:54] on [2:55] orally start thinking about what you're [2:57] doing writing a program [3:00] we should be thinking before we start [3:03] writing any code [3:06] how to think [3:09] when I favorite quotes is from the [3:12] cartoonist Guindon who says writing is [3:15] nature's way of letting you know how [3:17] sloppy your thinking is [3:20] to think to really think you have to [3:24] write if you're thinking without writing [3:27] chances are you're fooling yourself [3:29] we're only [3:31] pretending to think [3:34] what to write [3:36] well hunting a saber-toothed tiger sorry [3:41] writing wasn't invented then which meant [3:43] it was a very dangerous activity [3:46] building a house [3:49] well what'd you write before you build [3:52] to start building a house are called [3:54] blueprints and it's called drawing [3:56] blueprints [3:58] writing a program [4:00] well you should also write a blueprint [4:03] of your program but blueprints of [4:06] programs are what we call specifications [4:10] specifications [4:11] well when I say the word people tend to [4:15] panic this formal stuff that's [4:20] possible to read and understand and you [4:22] have to learn these funny symbols and [4:24] all that sort of stuff [4:25] yeah this is a blueprint you know all [4:28] these complicated stuff going on but [4:31] this is also a blueprint in [4:35] fact there's an entire spectrum of [4:38] blueprints [4:39] ranging from very [4:41] detailed complicated [4:44] blueprints which you need if you're [4:47] building something that's very big and [4:49] complicated at the other end they're [4:53] just rough sketch of what you're [4:55] building is just something you know [4:57] really simple that you're gonna do in [4:59] the weekend and [5:01] there are these ordinary blueprints for [5:05] you know things like a house or the [5:08] stuff that [5:10] kinds of blueprints that most of us [5:13] might have seen [5:14] and similarly there's a spectrum of [5:17] specifications [5:18] there are formal specifications and on [5:23] the other end of the your formal [5:26] specifications written in a in a precise [5:28] formal language and on the other and [5:30] they're just short pro specifications [5:33] and in the middle are what I like to [5:37] call mathematical pros [5:39] most code is really simple few sentences [5:44] of prose will do [5:49] some code is subtle [5:51] it requires more thought and [5:54] for that you have to write it spits [5:59] prose but it's it try to be you need to [6:02] be pretty precise and [6:05] some code is either very complex or very [6:09] subtle or critical and that's especially [6:13] the case if what you're doing is writing [6:16] concurrent or distributed system it is [6:19] going to be complex and subtle and quite [6:22] likely critical and for that you should [6:27] have tools you should be using tools to [6:29] check it to check your blueprints and [6:33] if you're going to use tools it means [6:36] you need a formal language because tools [6:38] don't understand prose [6:40] so how to write a spec well writing [6:44] requires thinking so how to think about [6:48] programs [6:51] we should think about programs like [6:54] computer scientists namely like [6:56] scientists [6:58] scientific thinking is a very successful [7:01] way of thinking we know all that it's [7:03] brought us what science does is make [7:07] mathematical models of reality [7:10] for example the first very successful [7:14] science was astronomy [7:17] the reality planets have mountains [7:20] oceans tides and weather and all sorts [7:23] of complicating things [7:24] but the simple model that's [7:30] gotten astronomy pretty far is to think [7:33] of a planet model the planet as a point [7:36] mass having position and momentum [7:40] computer science the reality consists of [7:44] digital systems things like a processor [7:47] chip a game console or a computer [7:50] executing a program [7:53] this is what I'll be talking about [7:55] computer is executing programs but what [7:58] I have to say applies to all those other [8:01] things just as well [8:03] so models there are lots of models that [8:07] you've probably come across Turing [8:09] machines partially ordered set of events [8:11] you probably haven't run across that and [8:14] lots of models [8:16] but - to my mind stand out as being the [8:21] most useful basic models [8:24] functions and [8:26] sequences of states [8:28] so functions [8:31] we model a function of a we can model a [8:35] program as a function that map's an [8:38] input or to an output or multiple inputs [8:42] - or possible inputs to possible outputs [8:46] in math the function is very simple it's [8:48] an order as a set of ordered pairs so [8:51] for example the square function on [8:54] natural numbers it's this set of ordered [8:57] pairs this pair 0 0 1 1 2 4 etc and the [9:03] element 2 4 is an element of this set of [9:07] the fact that the outlet that the pair 2 [9:11] 4 is an element of this square function [9:14] is usually expressed by saying square of [9:17] 2 equals 4 [9:19] now the domain of of square the domain [9:23] of a function are all the first elements [9:26] of those pairs so for the square [9:28] function the domain is the set [9:31] number 0 1 2 & 3 etc also known as the [9:36] natural numbers that I like to write as [9:38] net [9:39] suspect to define a function what you do [9:43] is you specify its domain so to specify [9:46] square we say the domain of square is [9:49] equal to the set of Naturals and for [9:52] each element X in its domain we specify [9:57] what Square map's X to and it maps the [10:01] value X to x squared so that defines the [10:05] square function [10:07] now functions in math are not the same [10:11] as functions in programming languages [10:13] math is much simpler I'm not a [10:15] programming language expert so I don't [10:17] know what those things that programming [10:21] languages call functions are but I know [10:24] what math is that methods are a lot [10:27] simpler [10:28] now the functions model will take you [10:31] very far it's very useful but er has [10:33] limitations [10:35] its limitation main limitation is that [10:38] it specifies what a program does but not [10:41] how it does it so for example quick sort [10:45] and bubble sort compute the same [10:47] function [10:49] but they're very different programs [10:53] also some programs don't just map inputs [10:57] to outputs [10:58] some programs run forever well you know [11:01] they may stop before the the Sun [11:04] explodes but it's useful to think of [11:06] them it's running forever for example [11:09] operating systems you can't specify an [11:11] operating system as a function because [11:13] it doesn't map inputs to outputs [11:15] so for that for those [11:19] specifications use what I like to call [11:21] the standard behavioral model in which a [11:24] program execution is represented by a [11:28] behavior a [11:30] behavior is a sequence of states and a [11:35] state is an assignment of values to [11:38] variables [11:39] and a program is modeled by a set of [11:43] behaviors which behaviors that represent [11:46] all possible executions of the program [11:49] so for example let's look at Euclid's [11:52] algorithm an algorithm is just an [11:55] abstract program [11:57] as you all undoubtedly know it computes [12:01] the greatest common divisor of two [12:02] natural of two integers m and n by [12:05] initializing X 2m and y2 M and then it [12:09] keeps subtracting the smaller from the [12:12] larger and [12:14] when x and y are equal then it stops [12:17] because that X and y equals the GCD of M [12:21] and n [12:22] so for M equals 12 and N equals 18 [12:25] there's just one possible behavior it [12:28] starts with a state in which that [12:31] assigns 12 to X and 18 to Y and then [12:35] subtract the next state is obtained by [12:37] subtracting the smaller one from the [12:39] larger X from Y so you get reach a state [12:43] where x equals 12 and y equals 18 minus [12:46] 12 or 6 and in the next state you [12:49] subtract the smaller which is y from the [12:51] larger and you wind up in the state in [12:53] which x equals 6 and y equals 6 and [12:56] since x and y are equal you stop so it's [12:59] very simple algorithm it has just one [13:02] possible behavior [13:04] so how do we describe a set of behaviors [13:08] well first of all there's a theorem that [13:12] says any set B of behaviors is [13:16] the conjunct the intersection of two [13:19] sets a set of behaviors satisfying a [13:22] safety property and a set of behaviors [13:26] satisfying a liveness property [13:28] so what our safety and liveness well I'm [13:32] not going to bother going into you know [13:33] the formal definitions but a safety [13:35] property is false if and only if it can [13:39] be violated at some point during the [13:41] behavior so partial correctness is an [13:44] example partial correctness is violated [13:47] if the program stops with the wrong [13:49] answer so that's some point in the [13:53] behavior you can tell that it that it [13:56] the property was violated the point at [13:58] which the program stopped with the [13:59] incorrect [14:01] answer the liveness property is one in [14:04] which you need to see the complete [14:06] behavior in order to know if it's false [14:08] or [14:10] if it's not satisfied by the by the [14:13] program classic example is termination [14:15] you can't tell that the program hasn't [14:18] terminated by looking at any finite [14:20] piece of the program you have to look at [14:22] the entire behavior to know that it [14:24] never terminates so we satisfy specify a [14:28] set of behaviors with by specifying a [14:31] safety property and aliveness property [14:33] now in practice specifying safety just [14:37] turns out to be more important because [14:40] that's where errors are most likely to [14:43] occur and tend to be more subtle I mean [14:45] liveness is important just not as [14:47] important to safety and so to save time [14:50] I'm going to ignore aliveness today and [14:53] just talk about safety so how to specify [14:57] a safety property we specify it with two [15:00] things the set of possible initial [15:03] States and the next state relation which [15:07] describes all possible successor states [15:10] of any state so what language should we [15:14] use to write these things [15:16] well let's act like scientists what [15:19] language do mathema two scientists use [15:22] and the language is mathematics that's [15:24] the language of science so the set of [15:27] initial states they're described by a [15:29] formula so in Euclid's algorithm the set [15:33] of initial States we initialize the [15:35] initial state is one in which X equals M [15:37] and y equals n so that is specified that [15:40] initial state is specified by this [15:42] formula and the only part this has only [15:46] one it's only one possible initial state [15:49] that satisfies that formula [15:51] the next state relation is also [15:54] described by a formula and to describe [15:57] it I'll use unprimed variables to talk [16:01] about the current state or the first [16:04] state of the pair and prime variables [16:07] we're talking about the next state so [16:09] let's look at the next state relation [16:11] for Euclid's algorithm there are two [16:14] possibilities either X is greater than Y [16:16] or Y is greater than X so that formula [16:19] is going to be the disjunction of two of [16:22] two formulas [16:24] if X is greater than Y then the new [16:28] value of x x prime is equal to what you [16:32] get by subtracting Y from it so it's new [16:35] value of x prime is equal to the old [16:38] value of X minus the old value of y and [16:40] the new value of X of Y is equal to the [16:44] old value so that first formula is [16:48] specified by all pairs of states [16:51] satisfying X greater than Y and in the [16:54] initial state in first state X prime [16:56] equals x minus y and y prime equals y [16:59] were [17:01] okay you understand what what I said and [17:06] that describes one one possibility or [17:09] the disjunction of these formulas to the [17:11] other case it's or what Y is greater [17:14] than X in the first state and the value [17:17] of y the new state and the value of y in [17:19] the old state minus the value of x in [17:21] the old state etc so this simple formula [17:24] describes the next state ratio relation [17:27] of Euclid's algorithm so [17:29] let's see how it works how you get [17:32] behaviors out of those two formulas [17:36] well for example for an example I'll [17:38] take em equal to 12 and N equals 18 so [17:42] to get the initial state we look at the [17:44] initial predicate s-- predicate and we [17:47] substitute [17:48] 12 for m and 18 for n for what for n [17:53] rather and you see you under that [17:55] substitution there's only one pair of [17:57] values x and y that satisfy this [17:59] relation x equals 12 and y equals 18 [18:03] so to get the next state the second [18:06] state we apply the next state look [18:09] formula substituting 12 for x and 18 for [18:13] y and we do that notice that 12 greater [18:17] than 18 is false and 18 greater than 12 [18:21] is true we'll false and anything is [18:24] false so that first half of the formula [18:27] is false true in anything is people to [18:31] the rest of the formula so this formula [18:34] is satisfied by the old value of x being [18:38] 12 and the new value of x being 18 minus [18:42] 12 or 6 [18:44] so this is the only possible next state [18:49] that satisfies this next relation when [18:53] for the initial state x equals 12 and y [18:57] equals 18 [18:58] okay to find the next state we do the [19:02] same thing we substitute 12 for X and [19:05] wife and 6 for y we simplify we see that [19:09] 6 greater than 12 is false and 12 [19:13] greater than 6 is true so the formula [19:15] simplifies to x prime equals 12 minus 6 [19:19] and y prime equals 6 [19:22] so it tells us that the only possible [19:25] successor state the only possible third [19:28] state is x equals 6 and y equals 6 and [19:31] to find the next state well do the same [19:35] thing we substitute 6 for X and 6 for y [19:38] and we see that the entire formula is [19:42] false and there are no values of x prime [19:45] and y prime which can make the formula [19:47] false true [19:49] so there is no next state so there's no [19:51] next state means the program is stopped [19:54] so that's Euclid's algorithm [19:57] and when we see special about Euclid's [20:00] algorithm is for any values of x and y [20:03] there are unique values of x prime and y [20:06] prime that make next true there's either [20:08] one value or no values [20:11] so Euclid's algorithm is deterministic [20:14] to model their non determinism we just [20:18] have a next state relation that allows [20:20] multiple next States for a current state [20:23] there's nothing magic you know terribly [20:25] difficult you know hard about can about [20:28] non determinism [20:30] multiple assignments of values to prime [20:32] variables that make next true for a [20:35] single assignment of values to unprimed [20:37] variables that's what non determinism is [20:40] all about okay what about formal specs [20:44] we need formal specifications only to [20:47] apply tools we mathematicians you know [20:50] before there were any tools you know [20:52] wrote math you know very you know an [20:54] informal notation they want to use [20:56] Mathematica though they have to you know [20:58] write in the language of Mathematica so [21:01] if we want to apply tools we need a [21:03] formal language and the language that [21:06] I'm going to describe were I use is [21:08] called TLA plus so this this pair of [21:13] formulas are written just like this in [21:17] TLA plus [21:20] we need to [21:22] form a language we need to write [21:25] declarations we declare X m and n to be [21:27] constants and x and y to be variables [21:30] and then add a little boilerplate we [21:33] said it extends the integers the other [21:35] juice or a standard module that defines [21:38] things like plus and minus and greater [21:40] than and we put them inside of a module [21:43] which I've called module Euclid and [21:44] that's a TL A+ specification of Euclid's [21:48] algorithm and this is what it looks like [21:50] in SK [21:53] now you can model check TLA plus specs [21:57] know model checking [22:00] conceptually checks all possible oceans [22:03] of [22:04] the program on a very small model [22:10] it's extremely effective and [22:15] quite easy to do you know you basically [22:18] tell the model checker what the model is [22:20] models are usually you know [22:22] instantiating values of constants so for [22:24] Euclid's algorithm we'd have to tell it [22:26] what m and n equals and then the model [22:28] checker will go through and in you know [22:31] a few nanoseconds for something that [22:33] simple will [22:34] we'll check whoa b1 possible their [22:38] behavior and you can write formal [22:41] correctness proofs and check them [22:43] mechanically in ela you know [22:46] write the proofs in ela plus and we have [22:49] a theorem prover that can check the [22:51] proofs and that's hard work and [22:54] well all this is math stuff we know he's [22:56] you know it's really very well nice and [22:59] pretty and stuff but we know that math [23:01] works only for toy examples to model [23:04] real system you need a real language [23:07] with types procedures objects and all of [23:10] that ma'am [23:12] wrong [23:14] let me quote something written by Chris [23:17] Newcomb as an Amazon engineer said we [23:21] have used TL A+ on 10 large complex [23:25] real-world systems in every case TL A+ [23:30] has added significant value either [23:32] preventing subtle serious bugs from [23:36] reaching production [23:38] or giving us enough understanding and [23:42] confidence to make aggressive [23:44] performance optimizations without [23:47] sacrificing correctness one of the other [23:50] things people will tell you about formal [23:52] methods is oh if you use a formal method [23:54] you're really going to kill your [23:55] performance well in fact it's just the [23:57] opposite and the other thing they'll [24:00] tell you is Oh management will never let [24:02] us do it well management at Amazon and [24:05] now encouraging teams to write TL a-plus [24:08] specs [24:09] and in annual planning managers are [24:13] allocating engineering time to use two [24:15] Plus [24:17] this was written in 2013 I think there [24:20] are a few more systems that they've been [24:22] specifying since then [24:23] the xbox360 memory system a [24:29] Chuck Thacker had an intern right at ela [24:32] plus spec of the Xbox memory system and [24:37] just writing the spec didn't even get to [24:40] point of model checking it he caught a [24:42] bug that the designers of the memory [24:48] system at IBM after scratching their [24:50] heads looking at it said yeah that was a [24:52] bug and that would not have been caught [24:55] by their testing procedures and that bug [24:58] would have caused every Xbox in the [25:00] world to crash after four hours of use [25:05] you can learn about TLA plus on the web [25:08] today I'm not going to be talking about [25:11] TLA plus I'm going to be talking about [25:13] informal specifications and I'll start [25:16] with an example [25:18] TLA Tech which is the pretty printer for [25:21] TL A+ [25:23] here is what somebody user TL A+ user [25:27] might write [25:28] some silly formula like this and if you [25:32] just did the naive output you know just [25:34] you know took things from you know [25:36] teletype font and put them into you know [25:39] translated the ascii into the symbols [25:42] left / right / into the conjunction [25:45] symbol and you get something like that [25:48] formula and the right [25:50] but [25:52] alignment is actually has significance [25:55] in TL a-plus and the user undoubtedly [25:59] wanted he certainly wanted the left [26:01] conjunction symbols aligned because the [26:04] formula might mean something different [26:06] otherwise and he also probably wanted [26:08] those equal signs aligned [26:10] so this is the right output that the [26:13] pretty partition produce for this [26:15] example [26:16] on the other hand if you look at this [26:18] input this is what the naive output [26:22] would be like and the user probably [26:25] didn't want those two symbols aligned [26:27] that was probably just an accidental [26:30] so in this case the naive output is the [26:33] correct output that's what you want [26:35] well there's no precise definition of [26:39] correct alignment correctness means what [26:42] the user wants and there's no way of [26:44] formally specifying what the user wants [26:48] so [26:49] obviously if we can specify correctness [26:52] specification what good can [26:54] specification we need to specify [26:55] correctness what could how can we use [26:57] specification well we can [27:00] not knowing what a program should do [27:03] doesn't mean you don't have to think you [27:06] just do this code it means you have to [27:08] think a lot harder [27:11] which means that that's what a spec is [27:14] even more important when you don't know [27:17] what the program is supposed to do and [27:19] it seems impossible to even say what it [27:21] should do because it has to do something [27:24] and you have to decide what it should do [27:25] so that requires a lot of thinking which [27:27] requires writing [27:29] so what did I do [27:32] well my spec consisted of six rules plus [27:37] definitions and these are in informal [27:40] met you know quote mathematical prose [27:43] and formal if they're written in [27:45] comments oh here's an example of one of [27:47] the rules it says a left comment token [27:50] is left comment aligned with its [27:52] covering token well I don't even know [27:55] what that means anymore I'd have to read [27:57] the spec but left comment aligned and [27:59] covering token are terms that were [28:03] defined in the in this specification I [28:06] wrote and if you look at it this is [28:09] really [28:10] mathematical prose [28:13] what did I write this spec well it was a [28:17] lot easier to understand and debug six [28:21] rules than 850 lines of code probably [28:26] would have been more than 850 lines of [28:28] code if I hadn't written those rules [28:29] first and I did a lot of debugging of [28:32] the rules [28:33] you know you write a bunch of rules and [28:36] you know what they're going to do is not [28:38] obvious and you try a bunch of examples [28:41] and I [28:42] a debugging code so I could see what [28:45] rules were being applied and we don't [28:48] would do with something weird and I say [28:50] oh and that rule needs to be modified [28:52] for this and an iterative process and [28:57] the few bugs and implementing the rules [28:59] were easy to catch though there was no [29:01] problem [29:03] had I just written the code it would [29:06] have taken me much longer to just get [29:09] something I could live with and I'm sure [29:12] the results wouldn't have produced [29:13] formatting nearly as good [29:16] so why not a formal spec why didn't I [29:18] write at tli + spec well several reasons [29:22] first of all getting it right was not [29:23] that important the world is not going to [29:26] come crashing down if something isn't [29:28] quite properly aligned and pretty [29:29] printing in particular it didn't have to [29:32] work in all corner cases yes No you know [29:35] there's no way of getting it you know [29:36] totally right and just as important [29:40] there aren't any tools that could help [29:42] me the model checker wouldn't have [29:44] helped in this example because you know [29:47] you need some some properties to check [29:49] it against and you know [29:52] they're just tools were not you know the [29:56] TLA plus tools are not designed for that [29:58] kind of problems so what's typical about [30:01] this spec the spec is at a higher level [30:05] than the code it could have been [30:07] implemented in any language [30:10] no method or tool none of the fate none [30:13] of your favorite ways of you know [30:16] programming methodologies would have [30:19] been worth a damn here they would not [30:22] have helped me write the spec [30:24] because [30:27] no method of writing you know better [30:30] code would have made the spec [30:32] unnecessary I had to write the spec it [30:36] was not code and it says nothing about [30:41] how to write the code you could [30:43] implement my spec in any language you [30:47] write a spec to help you think about the [30:50] problem before you think about the code [30:54] so what's not typical about this spec [30:57] it's quite subtle remember I said 95% of [31:01] code people write requires less thought [31:04] and you know simpler shorter specs good [31:07] enough [31:08] also it's a set of rules a set of rules [31:12] or requirements or axioms is usually a [31:14] bad spec because it's really hard to [31:16] understand it turns out that that was a [31:19] good match for this particular problem [31:21] which says you know [31:23] there are no universal rules about you [31:26] know how to write specs you know no [31:30] universal rules that'll tell you how to [31:32] write any program in the world [31:35] so specifying how to compute a function [31:39] specifying what the pretty printer [31:41] should do is hard [31:43] implementing the spec was easy [31:47] specifying what a sorting program should [31:50] do is easy [31:51] figuring out how to implement it [31:53] efficiently is hard at least if nobody [31:56] has showed you how [31:58] it requires thinking which requires [32:01] writing a specification [32:03] so I'll give you an example of a [32:06] specification for quicksort [32:09] probably you all seen quicksort [32:11] quicksort is written by Tony Hoare it's [32:14] a divide-and-conquer algorithm for [32:15] sorting an array call it a zero to a n [32:19] minus one and for simplicity I'll assume [32:21] that's an array of numbers and it uses a [32:24] partition procedure you give it two [32:26] arguments low and high and what that [32:29] procedure does is it chooses a pivot [32:32] point pivot number in low to high minus [32:35] 1 and then it permutes the elements to [32:39] the left of the pivot a low to a high [32:42] at it permutes [32:45] the [32:47] number the the part of the array within [32:50] the limit from low to high so that [32:53] everything from pivot downwards is [32:58] becomes is less than or equal to [33:00] everything from pivot plus 1 upwards [33:04] so fortunately if well seen quits before [33:07] so i [33:09] rambling description isn't necessary and [33:13] for this example I don't care how this [33:15] procedure is implemented of course [33:17] that's the real trick of quicksort [33:19] implementing that but you know I only [33:22] have 50 minutes here so so let's specify [33:25] quicksort and pseudocode [33:27] you know this by the partition procedure [33:30] is pick a pivot and low-to-high -1 and [33:33] permute a low to a high to make bla bla [33:35] bla and return the value pivot [33:38] and you have a recursive procedure q s [33:42] applied to low to high and if low is [33:44] less than high you call the protection [33:47] procedure for on low and high and then [33:52] you recursively call the procedure for [33:55] the 2 subintervals [33:57] and if low is not less than high then it [34:01] means you have one element and there's [34:05] nothing to sort and so you're done and [34:07] the main program just implies Q s to the [34:10] entire [34:11] array from Sirte and minus one so [34:16] informal no formal syntax no [34:19] declarations pseudocode it's it's a fine [34:23] specification yeah when you're gonna [34:25] code you know pretty trivial to code [34:27] from this and you know any programming [34:29] language [34:30] easy to understand [34:32] but it's a really quick sort [34:35] it's the way quicksort is almost always [34:37] described [34:39] but recursion is not a form fundamental [34:43] part of quicksort it's just one way of [34:46] implementing divide and conquer now [34:49] here's a problem and in fact it's [34:51] probably not the best way if you want to [34:53] execute this on multiple cores so here's [34:58] a problem give to your friends and [35:00] colleagues write a non recursive version [35:03] of quicksort [35:05] almost no one can do it in ten minutes [35:07] they try to compile the recursive [35:10] version and nobody can get almost nobody [35:12] can get that right and ten minutes [35:14] standing on their feet [35:16] but I'll tell you you know what the real [35:19] solution is [35:21] we do is maintain a set u of index [35:24] ranges on which partition needs to be [35:27] called [35:28] initially u equals contain is a set [35:32] containing a single [35:33] index range from zero and minus one [35:36] represented by this pair now we can [35:39] write this in pseudocode [35:40] but let's be scientists let's write an [35:43] it and next [35:45] directly in mathematics so the initial [35:49] predicate says a is equal to any array [35:53] of numbers of length n and u is equal to [35:57] the set consisting of that single [35:59] element yep this is in formal [36:02] mathematics but it's informal you know [36:05] we could write some formal way of [36:06] describing the set of a on any array of [36:10] numbers of length N and before writing [36:13] next let me make a definition [36:16] let me define partitions of B pivot low [36:19] high it's the set of arrays obtained [36:23] from B [36:24] by permuting the load to be high with [36:28] pivot point pivot in other words it's [36:31] the set of all values that the partition [36:34] procedure the set of all new values of a [36:37] that the partition procedure is allowed [36:39] to return if it's also returning pivot [36:43] as the pivot point and I won't bother [36:45] writing it out precisely [36:48] so next remember it's going to be a [36:51] relation between the old values of a of [36:53] U and the new values a prime and u Prime [36:56] so let's write it [36:58] first of all we're gonna stop if u is [37:02] the empty set so the next state relation [37:05] says U is not equal to the empty set and [37:08] so performing there's going to be false [37:10] if u is the empty set [37:13] then we pick any pair BT in U and [37:18] if B is not equal to T [37:22] then we pick any point P any number P [37:26] lying in B to t minus 1 and [37:29] we let a prime be any element of [37:34] the set of legal partitions you know the [37:37] results of the partition function that [37:40] if you've given it B and T is the input [37:43] and it returns P as the [37:46] partition as the pivot point and then u [37:52] prime is a set u we're finished with the [37:55] remai element bTW so we move that from [37:58] the set and then we add the 2 [38:00] subintervals [38:01] be P and you know to be and P plus 1 to [38:04] T into the set u of of [38:09] ranges that still need to be sorted [38:13] otherwise if B is not e is equal to T [38:15] then there's nothing to do we leave a [38:19] prime [38:21] unchanged and we let u prime equal u [38:25] with BT removed we just take BT out of [38:28] out of the set and [38:30] that's the next state relation that [38:33] describes a very nice way of doing [38:38] quicksort iterative iteratively it's [38:42] actually a more general algorithm [38:45] because if you look at all the behaviors [38:46] that can produce [38:48] the set of behaviors produced by the [38:50] recursive version is a sub set that is [38:54] the set of all sequences of values that [38:56] a assumes is going to be a subset of the [38:59] ones that [39:01] this [39:04] next state relation allows [39:06] so why can almost no one find this [39:09] version of quicksort [39:12] because people's minds are stuck in code [39:16] they haven't thought learn to think at a [39:20] higher level than the code [39:23] now it's easy to write this as a precise [39:26] formula pick any [39:29] arbitrary value is really existential [39:32] quantification the formula is true if [39:35] there exists a value B T and you [39:38] similarly there and [39:42] just let letting a prime be any element [39:45] of this set well that's easy enough to [39:47] write is a prime and an element of that [39:50] and those relations new values of U [39:52] prime those thinks the with pros to the [39:55] right of the equal sign they're easy to [39:59] express whoops [40:06] this way and so on then so [40:11] what you have is a TL a plus formula [40:14] here [40:17] perfectly formal if you prefer [40:21] pseudocode [40:23] we have plus Cal it looks like a toy [40:27] programming language and the algorithm [40:30] in fact appears in a comment and tij [40:32] plus module but an expression can be any [40:35] TL a plus expression it also has some [40:39] constructs for non determinism but the [40:41] fact that it can be any TL a an [40:43] expression can be any TL a plus [40:44] expression which means any expression of [40:47] mathematics makes it enormous ly [40:49] powerful more more expressive than [40:51] anything any programming language has [40:55] designer has ever dreamed of and it gets [40:58] compiled to an easy-to-understand TL [41:01] a-plus spec and in fact I regularly [41:04] write things in plus cow and then when [41:07] I'm proving the correctness of them I [41:08] reason directly about the TLA plus [41:11] specification and you like the TLA plus [41:13] tools the model checker and theorem [41:15] prover to the plus to the translation [41:20] programs that run forever have been [41:23] talking about programs that compute a [41:25] function [41:26] programs that run forever usually [41:28] involve concurrency things like [41:31] operating systems distributed systems [41:33] few people can get them right by just [41:36] thinking and writing I'm not one of them [41:40] we need tools to check what we're doing [41:45] the reason the TLA plus was so useful to [41:48] the Amazon engineers is because the [41:51] model checker [41:52] so for concurrent and distributed [41:56] systems you need to write something you [41:58] use TLA plus or plus Cal it was designed [42:01] for distributed systems it's great and [42:04] you're not gonna get it you know [42:06] distributed concurrent algorithm right [42:08] if you don't model check it it's spent [42:10] you don't write a specification and [42:12] check it [42:14] the other 95% really simple stuff here [42:18] is an example of a spec you know that I [42:22] write wrote in a program [42:25] why did I write that spec to be sure I [42:28] knew what the code should do before [42:30] writing it without writing aspect I only [42:33] thought it was obvious what it should do [42:36] I had to write the spec to be sure it [42:39] was really as simple and as obvious as I [42:41] thought it was and later I didn't have [42:45] to read the code to know what that piece [42:48] of code I wrote did I just wrote the [42:50] specification could rig the [42:52] specification so a general rule that's [42:55] one rule at least we can say about specs [42:58] in general if you're writing a spec of [43:00] what a code should do what a piece of [43:03] code should do that should say [43:05] everything that anyone needs to know to [43:08] use the code [43:11] how the code worked in the example I've [43:13] just showed was just too simple to [43:15] require a spec I mean it was it turned [43:18] out to be harder than I thought I would [43:20] have discovered that when I started [43:21] coding and I would have stopped coding [43:23] and wrote and written a specification [43:26] so what programmers should know about [43:29] thinking what everyone should know about [43:32] thinking [43:33] everyone thinks they think [43:36] but if you don't write down your [43:38] thoughts [43:39] you're fooling yourself [43:42] what programmers should know about [43:44] thinking is that you should think before [43:47] you code [43:51] which means you should write before you [43:54] code a spec is simply what you write [43:58] before coating [43:59] so what coach you know what do I mean by [44:02] code what should you specify basically [44:05] any piece of code that someone else [44:07] might want to use or modifying and that [44:11] somebody else is likely to be you in a [44:13] month when you've forgotten what this [44:14] piece of code you've written does [44:16] it could be an entire program or system [44:19] a class a method just a tricky piece of [44:23] code inside of a method [44:26] what should you specify about the code [44:29] what it does [44:32] which means everything anyone needs to [44:34] know to use it and perhaps how it does [44:38] it if it's complicated subtle you need [44:41] to think hard about in order to get it [44:43] right [44:44] this is you know what's sometimes called [44:46] an algorithm or a high-level design [44:50] how should you think about [44:53] or specify your code [44:56] above the code level in terms of states [45:00] and behaviors or functions for input [45:04] output relations should do it [45:06] mathematically as [45:08] rigorously informally as is necessary [45:12] you should be thinking mathematically [45:14] even if you're writing thing the [45:17] mathematics very informally [45:20] perhaps with pseudocode or pass or plus [45:23] Cal if you're specifying how something [45:25] does it [45:26] so how do you learn to write specs [45:29] by writing formal specs [45:33] even if you don't even if you never need [45:36] to write a completely formal spec in [45:38] your life learning to write formal specs [45:41] will really help you learn to write the [45:44] informal specs that you need to write [45:46] you learn to write programs by writing [45:49] them running them and correcting your [45:51] errors you can learn to write formal [45:54] specs by writing them running them with [45:57] a model checker and correcting your [45:59] errors [46:00] now TL A+ may not be the best language [46:03] for you you know it's I'm sure it's not [46:06] the best language for formal language [46:08] for every kind of specification in the [46:10] world no language can be it may not be [46:13] the best form of language you know for [46:15] what your particular needs are but I do [46:18] know that it's great for learning to [46:20] think mathematically [46:21] so unless you can find something better [46:24] for doing that you know which works [46:28] use TL a plus you know learn TL a plus [46:31] learn to think mathematically [46:34] how do you connect the spec to the code [46:37] well you have comments connecting [46:40] mathematical concepts and their [46:41] implementation for example the [46:43] mathematical concept might be a graph [46:45] and the implementation would be an array [46:48] of node objects and an array of link [46:51] objects in this connection the other [46:54] specification especially if it's an [46:57] informal specification should be in the [46:58] comments and so should the explanation [47:01] of how those mathematical comments are [47:03] implemented in the code so what about [47:07] coding I mean we're programs involve [47:09] writing code you have to write code I [47:11] have nothing to say about writing code [47:13] nothing I said implies anything you [47:17] still have to think while you code which [47:20] means you have to write while you're [47:22] coding but what you're right while [47:24] you're coding is is code [47:26] and I have nothing to say about how you [47:29] should code use any programming language [47:30] you want any programming methodology [47:33] you still you're still going to have to [47:36] test and debug your program not this [47:39] good writing aspect is we're going to [47:41] catch coding errors because it's not [47:43] about coding it'll catch algorithm [47:46] errors [47:48] writing specs is an additional step and [47:51] it may save time by catching errors [47:54] early when they're easier to correct no [47:56] promises there it certainly will improve [47:59] you programming so you'll be writing [48:02] better programs [48:03] why are programmers reluctant to write [48:06] specs well writing is hard writing is [48:09] hard in fact because thinking is hard [48:10] you know there's no Royal Road to [48:13] mathematics as [48:14] who wasn't who said that Archimedes I [48:17] think [48:19] why is writing hard well because writing [48:22] requires thinking and thinking is hard [48:24] and as I said it's easier to think [48:27] you're thinking writing is a lot like [48:29] running unless you do it slower you are [48:33] and you have to strengthen your writing [48:35] muscles you know just like you have to [48:37] strengthen your rust and you're running [48:38] muscles it takes practice and it's [48:42] easier to find an excuse not to [48:44] and one of the nicest excuses well what [48:47] if the spec is wrong maybe you made a [48:49] mistake in the spec maybe the [48:51] requirements change where some [48:53] enhancement is needed in fact [48:56] and the code will have to be changed [48:59] maybe even before the program is [49:01] finished in fact [49:05] changing the code is something that [49:07] eventually happens to all useful [49:09] programs if your program is useful it's [49:13] gonna have users the users are going to [49:15] find things extra things they want of it [49:17] the things that don't work the way you [49:20] know you thought was a good idea be your [49:22] customers you know decide otherwise this [49:25] happens eventually happens to all useful [49:27] programs in an ideal world you'd write a [49:31] new spec and then you know the code [49:33] you'd completely rewrite the span of the [49:35] spec well we all know this isn't gonna [49:38] happen in the real world the code is [49:41] patched and if you're lucky the spec is [49:44] updated [49:45] well if this is inevitable why bother [49:48] writing specs in the first place if you [49:50] know this little lovely thing is you [49:53] know the spec turns out you know - has [49:55] to be changed and you know we're back in [49:58] our old messy world well there's two [50:01] reasons the first reason is that whoever [50:04] has to modify the code will be eternally [50:08] grateful for every word or formula of [50:11] specification that you write and [50:13] whoever maybe you believe me I've you've [50:16] done that you know coming back and don't [50:20] have time for second stories but [50:21] particularly you know modifications and [50:23] the pretty print or in fact [50:25] and that's why you should update the [50:29] spec when you change the code [50:31] the second reason is that every time [50:35] code is patched it becomes a little [50:38] uglier harder to understand and harder [50:41] to maintain [50:43] and if you but if you don't start with a [50:47] spec every piece of code you write is a [50:51] patch [50:52] which means the program starts out from [50:55] the beginning being ugly hard to [50:58] understand and hard to maintain and then [51:00] God help you when you do have to [51:02] maintain you know try to unfit out what [51:05] it's doing in order to [51:06] to make changes as [51:10] Dwight Eisenhower said no battle was [51:14] ever won according to plan [51:16] but no battle was ever won with that one [51:20] so some people will tell you that [51:22] writing specs is a waste of time in some [51:26] situations and maybe [51:28] sometimes there's no need to think about [51:30] what you're doing maybe something is [51:31] really so trivial that you know doesn't [51:34] need much thought [51:35] but we'll remember [51:37] when they're telling you not to write a [51:39] spec they're telling really telling you [51:42] not to think and [51:45] you know faking is a really good idea [51:48] and don't trust anybody who tells you [51:50] not to do it [51:52] thinking doesn't guarantee that you [51:54] won't make mistakes [51:55] but not thinking generally guarantees [51:58] that you will [52:00] find out more about TLA plus go to my [52:03] home page and click on the link to the [52:06] TLA web page thank you [52:09] [Applause] [52:15] I think there's time for a few questions [52:21] very few I think [52:23] one yeah yes I [52:30] hope that's not a judgment on my talk [52:35] thanks for your talk how about [52:37] specification by example it's a [52:40] pragmatic approach that is a little bit [52:42] inspired by test-driven development [52:45] which [52:47] since it's not complete of course but [52:51] since completeness anyway is undecidable [52:54] it's a good pragmatic approach so do you [52:58] know so terrible pragmatic approach it's [53:01] maybe a good maybe a good way to explain [53:03] what something examples are great to [53:05] explain them but they're terrible they [53:08] keep you from thinking you think in [53:10] terms of these particular examples the [53:12] real problem in programming is not to [53:14] think of your the easy cases the obvious [53:17] cases the nice case if the hard cases [53:19] and you don't get that with examples and [53:22] boy I'll tell you I you to eclipse [53:24] programming and I've [53:28] you know [53:29] there's so much so many methods of [53:32] eclipse that are explained by here's an [53:36] here's an example of code using it and I [53:39] have yet to manage to use those methods [53:42] to do it what I want to do because it [53:45] doesn't work I find that it just doesn't [53:47] work on anything other than that example [53:48] and I have no idea why not not a program [53:52] you know programming by example I don't [53:54] know about you know if that works but a [53:56] specification by example it doesn't [54:09] thank you for the great talk um so what [54:12] happens once you patch the code do you [54:13] suggest keeping the old specs around or [54:16] do you just get rid of him like does it [54:18] help to have context [54:24] well first of all I think whenever I [54:26] well whenever I do fix bugs and that may [54:30] require changing the spec I will also [54:32] take a note as to why I change the spec [54:35] because you know two years later I'm [54:38] going to come back and say why did I do [54:40] that [54:41] about whether you know to what extent [54:43] that's useful for the specification [54:47] itself I don't know certainly you're [54:51] always a good idea not to throw anything [54:53] away you know we shouldn't be throwing [54:55] you know information away these days you [54:57] know it doesn't cost anything to save it [54:59] so you should certainly save the old [55:01] specs whether you should you know [55:04] whether you should mention the change in [55:07] the in the new spec I don't I don't have [55:10] any any good answer to that [55:17] without wanting to [55:21] be rude or anything I think most [55:23] programmers don't use formal [55:25] specifications anymore my PhD was in [55:27] that background most [55:29] programmers these days they take [55:31] algorithms they take code they adapt it [55:33] to what they want to do and that's 99 [55:36] percent is from my perspective how most [55:38] software is built these days [55:40] that we actually really be nice if it if [55:44] that where it can build Bell built well [55:48] that way [55:49] it doesn't work because [55:51] they're trying to use I find that it [55:54] doesn't work because the other pieces of [55:57] code that I want to use don't have [56:00] specifications so I really can't use [56:02] them very well I do the best I can and I [56:05] wind up having to spend a [56:08] hell of a lot of time [56:10] trying to figure out what this piece of [56:13] code does [56:14] which something I shouldn't have to do [56:17] if it had been specified [56:19] again I've said that you know 95% of [56:22] programming [56:23] you know requires very little [56:26] specification and [56:28] you know a few lines [56:31] you know are usually enough but you know [56:35] you are going to come up with this 5% [56:36] which isn't just you know take this [56:38] thing and put it here and combine it [56:40] with this thing there's gonna be [56:41] something some things code you're gonna [56:43] have to write that is subtle it has some [56:47] some a bit of difficulty in it and you [56:50] want to be able to you know you want to [56:52] be prepared for that because we use the [56:54] same techniques that you use you're the [56:56] same lack of thought that you use when [56:58] you're doing that other 95% you're gonna [57:01] screw up badly [57:12] unfortunately I have never really looked [57:14] into TLA plus but having studied [57:18] temporal logic it is inevitable to to [57:22] fail to to notice that that there's a [57:25] lot of temporal logic going on [57:27] underlying what you've been saying so I [57:30] was wondering [57:33] have you studied probably you have the [57:37] possibilities for using more directly [57:39] the language of temporal logic will [57:41] future operates as necessity operators [57:44] and all that stuff and so ok TLA stands [57:48] for the temporal logic of actions [57:52] temporal logic is evil I [57:55] can tell you explain why because it's [57:58] very hard to use but it turns out it's a [58:01] necessary evil [58:02] temporal logic turns out to be the best [58:05] way of thinking about liveness and [58:08] so if you use TLA plus when you get to [58:12] the liveness part you're going to be [58:13] using temporal logic and be aware of it [58:15] but remember that I said that safety is [58:18] more important and it turns out that you [58:20] do not need to think you got not need to [58:23] know temporal logic in order to handle [58:25] safety and what the reason I think you [58:29] know that TL A+ is better than other [58:32] kinds of temporal logic is that you know [58:36] ascent need-to-know essentially no [58:38] temporal logic to use it for safety [58:40] if you look at the actual specification [58:43] there is the specific the tli [58:45] specification I've noticed I've written [58:47] it it's two formulas in it and next well [58:49] TLA allows you to combine them into a [58:52] single temporal logic formula which you [58:55] you know you do at the end eventually [58:57] and but the idea of the thing it makes [59:00] it great is that you'll write a thousand [59:03] line specification 999 lines are going [59:07] to be ordinary math initial predicate [59:09] next state relation and then the last [59:10] line is going to be a temporal logic [59:12] formula so okay [59:16] well do we have no [59:20] so we are now at the end of our plenary [59:25] session and I'd like to thank you very [59:27] much but before we do that just to [59:30] remind you all we got the buses outside [59:32] to go out to the dinner tonight take [59:36] your bags with you there will be space [59:37] there for your bags and then we'll be [59:40] going back to the hotels or coming back [59:42] here and now thank you very much Leslie [59:45] [Applause]