Skip to content

Commit cfaf84d

Browse files
authored
Add TLA+ Specification Review Guidelines (#473)
- Introduced a new document outlining best practices for reviewing and validating TLA+ specifications. - Covers key areas such as model checking, simulation, specification structure, constants, variables, invariants, properties, fairness constraints, and documentation standards. - Provides a systematic approach for reviewers to evaluate TLA+ specifications using a producer-consumer example. - An invariant is a better alternative than Assert. - Includes examples for bug injection strategies. - Clarify coverage statistics reporting in TLC, including usage of the `-coverage 1` option for model checking. - References new guide on Functions, Records, and Sequences in TLA+ - Introduced a new document detailing the definitions, modifications, and usage of functions, records, and sequences in TLA+. - Included examples for defining functions, accessing values, modifying functions with `EXCEPT`, and recursive functions. - Explained the relationship between sequences and records as specific cases of functions. [Documentation] Signed-off-by: Markus Alexander Kuppe <[email protected]>
1 parent b5c5b9a commit cfaf84d

File tree

2 files changed

+457
-0
lines changed

2 files changed

+457
-0
lines changed
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
---
2+
title: Functions, Records, and Sequences in TLA+
3+
description: A comprehensive guide to functions in TLA+, including how to define and modify them, and understanding records and sequences as special cases of functions.
4+
---
5+
6+
## Functions, Records, and Sequences in TLA+
7+
8+
In TLA+, **functions** are fundamental data structures that map values from a **domain** to a **codomain** (also called range). If you come from a programming background, think of functions as **maps**, **dictionaries**, or **associative arrays**.
9+
10+
> 🧠 Unlike operators, functions are *first-class values* in TLA+—they can be stored in variables, passed as arguments, and quantified over.
11+
12+
---
13+
14+
### 🔧 Defining Functions
15+
16+
A function is defined using the **mapsto** syntax: `[x \in S |-> expr]`
17+
18+
```tla
19+
\* A function that doubles each natural number
20+
double == [n \in Nat |-> 2 * n]
21+
22+
\* A function mapping people to their ages
23+
ages == [p \in {"Alice", "Bob", "Carol"} |->
24+
IF p = "Alice" THEN 30
25+
ELSE IF p = "Bob" THEN 25
26+
ELSE 28]
27+
```
28+
29+
**Accessing function values** uses square bracket notation:
30+
31+
```tla
32+
double[5] \* Evaluates to 10
33+
ages["Alice"] \* Evaluates to 30
34+
```
35+
36+
You can retrieve a function's domain with `DOMAIN`:
37+
38+
```tla
39+
DOMAIN ages \* Evaluates to {"Alice", "Bob", "Carol"}
40+
```
41+
42+
---
43+
44+
### 📐 Set of All Functions: `[A -> B]`
45+
46+
TLA+ provides syntax to define the **set of all functions** from domain `A` to codomain `B`:
47+
48+
```tla
49+
[A -> B]
50+
```
51+
52+
This is the set of *all possible* functions that map every element of `A` to some element of `B`.
53+
54+
#### Example
55+
56+
```tla
57+
\* All functions from {1, 2} to {TRUE, FALSE}
58+
AllMappings == [{1, 2} -> {TRUE, FALSE}]
59+
60+
\* This set contains 4 functions:
61+
\* [1 |-> TRUE, 2 |-> TRUE ]
62+
\* [1 |-> TRUE, 2 |-> FALSE]
63+
\* [1 |-> FALSE, 2 |-> TRUE ]
64+
\* [1 |-> FALSE, 2 |-> FALSE]
65+
```
66+
67+
> 💡 The size of `[A -> B]` is `|B|^|A|` (cardinality of B raised to cardinality of A).
68+
69+
---
70+
71+
### ✏️ Modifying Functions with `EXCEPT`
72+
73+
Since TLA+ is mathematical, you don't "mutate" a function. Instead, you create a **new function** based on an existing one using `EXCEPT`.
74+
75+
```tla
76+
\* Original function
77+
ages == [p \in {"Alice", "Bob"} |->
78+
IF p = "Alice" THEN 30 ELSE 25]
79+
80+
\* New function with Bob's age updated
81+
ages2 == [ages EXCEPT !["Bob"] = 26]
82+
```
83+
84+
After this, `ages2["Bob"]` evaluates to `26`, while `ages["Bob"]` remains `25`.
85+
86+
#### Using `@` to Reference the Old Value
87+
88+
The special symbol `@` refers to the **previous value** at that position:
89+
90+
```tla
91+
\* Increment Alice's age by 1
92+
ages3 == [ages EXCEPT !["Alice"] = @ + 1]
93+
\* ages3["Alice"] is now 31
94+
```
95+
96+
#### Multiple Updates
97+
98+
You can update multiple keys in a single `EXCEPT`:
99+
100+
```tla
101+
ages4 == [ages EXCEPT !["Alice"] = 31, !["Bob"] = 26]
102+
```
103+
104+
#### Nested Function Updates
105+
106+
For functions of functions (nested structures), use chained `!` notation:
107+
108+
```tla
109+
\* A nested structure: departments -> employees -> salaries
110+
company == [dept \in {"Engineering", "Sales"} |->
111+
[emp \in {"Alice", "Bob"} |-> 50000]]
112+
113+
\* Give Alice in Engineering a raise
114+
company2 == [company EXCEPT !["Engineering"]["Alice"] = 75000]
115+
```
116+
117+
> ⚠️ **Performance Note:** TLA+ is mathematics, not a programming language. Using `EXCEPT` does not "efficiently update" a function—it defines an entirely new function. There is no computational advantage to using `EXCEPT` over defining a fresh function. However, `EXCEPT` is often *clearer* and more concise.
118+
119+
---
120+
121+
### 🔄 Recursive Functions
122+
123+
TLA+ allows defining functions that refer to themselves using the **recursive function definition** syntax:
124+
125+
```tla
126+
f[x \in S] == expr \* where expr may reference f
127+
```
128+
129+
This is distinct from recursive *operators* (which use the `RECURSIVE` keyword). A recursive function definition creates a function value that can reference itself.
130+
131+
#### Example: Factorial
132+
133+
```tla
134+
factorial[n \in Nat] == IF n = 0 THEN 1 ELSE n * factorial[n - 1]
135+
```
136+
137+
Here, `factorial` is a function from `Nat` to `Nat` that refers to itself in its definition.
138+
139+
#### Example: Fibonacci
140+
141+
```tla
142+
fib[n \in Nat] == IF n <= 1 THEN n ELSE fib[n - 1] + fib[n - 2]
143+
```
144+
145+
#### Example: Sum of a Sequence
146+
147+
```tla
148+
EXTENDS Sequences, Naturals
149+
150+
\* Sum elements of a sequence (using indices)
151+
sum[s \in Seq(Nat)] ==
152+
IF s = <<>> THEN 0
153+
ELSE Head(s) + sum[Tail(s)]
154+
155+
\* sum[<<1, 2, 3>>] evaluates to 6
156+
```
157+
158+
#### Multiple Parameters
159+
160+
For functions with multiple parameters, use a tuple or record as the domain:
161+
162+
```tla
163+
\* Power function using a tuple domain
164+
power[pair \in Nat \X Nat] ==
165+
LET base == pair[1]
166+
exp == pair[2]
167+
IN IF exp = 0 THEN 1 ELSE base * power[<<base, exp - 1>>]
168+
169+
\* power[<<2, 3>>] evaluates to 8
170+
```
171+
172+
> 💡 **Tip:** Many recursive patterns over sequences and sets can be expressed more elegantly using operators from the `Folds` community module, such as `FoldSeq` and `FoldSet`.
173+
174+
---
175+
176+
### 📜 Sequences: Functions with Natural Number Domains
177+
178+
**Sequences** are functions whose domain is `1..n` (the set `{1, 2, ..., n}`) for some natural number `n`. They represent ordered lists.
179+
180+
#### Defining Sequences
181+
182+
```tla
183+
EXTENDS Sequences
184+
185+
\* A sequence of three elements
186+
colors == <<"red", "green", "blue">>
187+
188+
\* Equivalent to:
189+
colors == [i \in 1..3 |->
190+
IF i = 1 THEN "red"
191+
ELSE IF i = 2 THEN "green"
192+
ELSE "blue"]
193+
```
194+
195+
#### Accessing Elements
196+
197+
```tla
198+
colors[1] \* Evaluates to "red"
199+
colors[2] \* Evaluates to "green"
200+
Len(colors) \* Evaluates to 3
201+
DOMAIN colors \* Evaluates to {1, 2, 3}
202+
```
203+
204+
> ⚠️ Sequences are **1-indexed**, not 0-indexed!
205+
206+
#### Set of All Sequences
207+
208+
```tla
209+
\* All sequences of elements from set S
210+
Seq(S)
211+
212+
\* Example: all sequences of bits
213+
Seq({0, 1}) \* Contains <<>>, <<0>>, <<1>>, <<0,0>>, <<0,1>>, ...
214+
```
215+
216+
> Note: `Seq(S)` is an *infinite* set (for non-empty `S`), so TLC cannot enumerate it directly. Use bounded versions like `BoundedSeq(S, n)` from SequencesExt.
217+
218+
---
219+
220+
### 📦 Records: Functions with String Domains
221+
222+
**Records** are simply functions whose domain is a set of strings (field names). TLA+ provides convenient syntax for working with them.
223+
224+
#### Defining Records
225+
226+
```tla
227+
\* A record with fields "name" and "age"
228+
person == [name |-> "Alice", age |-> 30]
229+
230+
\* Equivalent to:
231+
person == [f \in {"name", "age"} |->
232+
IF f = "name" THEN "Alice" ELSE 30]
233+
```
234+
235+
#### Accessing Fields
236+
237+
Use dot notation or bracket notation:
238+
239+
```tla
240+
person.name \* Evaluates to "Alice"
241+
person["name"] \* Also evaluates to "Alice"
242+
```
243+
244+
#### Set of All Records
245+
246+
```tla
247+
\* All possible person records
248+
People == [name: {"Alice", "Bob"}, age: {25, 30, 35}]
249+
250+
\* This is the set of all records with:
251+
\* - "name" field from {"Alice", "Bob"}
252+
\* - "age" field from {25, 30, 35}
253+
\* Contains 2 * 3 = 6 records
254+
```
255+
256+
> 🔍 Note the syntax difference: `:` for record sets, `|->` for specific records.
257+
258+
#### Updating Records with `EXCEPT`
259+
260+
```tla
261+
person2 == [person EXCEPT !.age = 31]
262+
\* Or equivalently:
263+
person2 == [person EXCEPT !["age"] = 31]
264+
```
265+
266+
---
267+
268+
### 🧰 Useful Modules
269+
270+
TLA+ provides operations for functions and sequences across several modules:
271+
272+
| Module | Description |
273+
|--------|-------------|
274+
| `Sequences` | Standard module with basic sequence operations (`Len`, `Head`, `Tail`, `Append`, `SubSeq`, `\o`) |
275+
| `Functions` | Community module with function utilities (`Range`, `Restrict`, `Inverse`, injections, surjections, bijections) |
276+
| `SequencesExt` | Community module with extended sequence operations (`ToSet`, `SetToSeq`, `Reverse`, `FlattenSeq`, `BoundedSeq`) |
277+
| `Folds` | Community module with fold operations over sequences and sets (`FoldSeq`, `FoldSet`) |
278+
279+
#### How to Explore Module Contents
280+
281+
**In VS Code / Cursor:** Add an `EXTENDS` statement and Ctrl+Click (Cmd+Click on macOS) on the module name to navigate to its source:
282+
283+
```tla
284+
EXTENDS Sequences, Functions, SequencesExt
285+
```
286+
287+
**On the web:** Browse the standard modules at [github.com/tlaplus/tlaplus](https://github.com/tlaplus/tlaplus/tree/master/tlatools/org.lamport.tlatools/src/tla2sany/StandardModules) and CommunityModules at [github.com/tlaplus/CommunityModules](https://github.com/tlaplus/CommunityModules/tree/main/modules).
288+
289+
**For AI assistants:** Use the TLA+ MCP tools `tlaplus_mcp_sany_modules` to list available modules, and `tlaplus_mcp_sany_symbol` to extract symbols from a specific module.
290+
291+
---
292+
293+
### 🧠 Summary
294+
295+
| Concept | Domain | Syntax | Access |
296+
|---------|--------|--------|--------|
297+
| **Function** | Any set | `[x \in S \|-> expr]` | `f[x]` |
298+
| **Sequence** | `1..n` | `<<e1, e2, ...>>` | `s[i]` (1-indexed) |
299+
| **Record** | Strings | `[field1 \|-> v1, ...]` | `r.field` or `r["field"]` |
300+
301+
| Set of... | Syntax |
302+
|-----------|--------|
303+
| All functions A → B | `[A -> B]` |
304+
| All sequences | `Seq(S)` |
305+
| All records | `[field1: S1, field2: S2, ...]` |
306+
307+
> 🎯 **Key Insight:** Records and sequences are not special types—they're just functions with specific domains. This unification is a hallmark of TLA+'s elegant mathematical foundation.

0 commit comments

Comments
 (0)