quint-lang
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseQuint Language Reference
Quint语言参考手册
Quint is an executable specification language for complex systems, developed by Informal Systems. It compiles to TLA+ and supports simulation and model checking.
Quint是由Informal Systems开发的面向复杂系统的可执行规范语言,可编译为TLA+,并支持模拟与模型检查。
Module structure
模块结构
quint
module MyProtocol {
// type aliases, constants, state, actions, properties
}Modules can import others:
quint
import Voting.* // all definitions
import Voting(quorum) // specific definition
import Voting as V // namespace aliasquint
module MyProtocol {
// 类型别名、常量、状态、动作、属性
}模块可导入其他模块:
quint
import Voting.* // 导入所有定义
import Voting(quorum) // 导入特定定义
import Voting as V // 命名空间别名Types
类型
| Type | Description | Example |
|---|---|---|
| Integers | |
| Booleans | |
| Strings | |
| Finite set | |
| Ordered sequence | |
| Key-value map (type is | value: |
| Tuple | |
| Record | |
| Sum (variant) | (use type alias) |
Type aliases:
quint
type NodeId = int
type Phase = Idle | Propose | Vote | Commit // enum — prefer over string literals| 类型 | 描述 | 示例 |
|---|---|---|
| 整数 | |
| 布尔值 | |
| 字符串 | |
| 有限集合 | |
| 有序序列 | |
| 键值映射(类型为 | 值: |
| 元组 | |
| 记录 | |
| 求和类型(变体) | (使用类型别名) |
类型别名:
quint
type NodeId = int
type Phase = Idle | Propose | Vote | Commit // 枚举——优先于字符串字面量Definitions
定义
quint
// Module parameter — fixed at instantiation, not a state variable
const N: int
const Nodes: Set[str]
// Pure function — no state access, usable anywhere
pure def max(a: int, b: int): int = if (a > b) a else b
// Stateful operator — can read vars, takes arguments (unlike val)
def isActive(n: str): bool = active.contains(n)
// State-reading value — can read vars, no arguments
val quorum: bool = votes.size() * 2 > nodes.size()
// Compile-time constant
pure val N: int = 4
val threshold: int = N / 2 + 1constpure valconstimport A(N = 3)pure valdefvaldefvalquint
// 模块参数——实例化时固定,不是状态变量
const N: int
const Nodes: Set[str]
// 纯函数——无状态访问,可在任意位置使用
pure def max(a: int, b: int): int = if (a > b) a else b
// 有状态运算符——可读取变量,接受参数(与val不同)
def isActive(n: str): bool = active.contains(n)
// 读取状态的值——可读取变量,无参数
val quorum: bool = votes.size() * 2 > nodes.size()
// 编译时常量
pure val N: int = 4
val threshold: int = N / 2 + 1constpure valconstimport A(N = 3)pure valdefvaldefvalState variables
状态变量
quint
type LocalState = {
leader: int,
phase: Phase, // enum (see Type aliases) — prefer over a bare str
votes: Set[int],
log: List[str],
}
var localState: LocalState // cohesive local protocol state
var peers: int -> str // independent concern (peer metadata)State variables can only be read in definitions and actions; they cannot be read in .
valpure defquint
type LocalState = {
leader: int,
phase: Phase, // 枚举(见类型别名)——优先于裸字符串
votes: Set[int],
log: List[str],
}
var localState: LocalState // 内聚的本地协议状态
var peers: int -> str // 独立关注点(节点元数据)状态变量仅能在定义和动作中读取;无法在中读取。
valpure defActions
动作
Actions describe state transitions. They return — if the action fires.
booltruequint
action init: bool = all {
leader' = 0,
phase' = Idle,
votes' = Set(),
log' = List(),
state' = Map(),
}
action propose(node: int): bool = all {
phase == Idle,
node > 0,
leader' = node,
phase' = Propose,
votes' = votes,
log' = log,
state' = state,
}Key rules:
- Every must be assigned in every action (use
varto leave unchanged).x' = x - — all sub-expressions must hold (conjunction). Guards are plain boolean expressions inside
all { ... }.all { } - — at least one must hold (disjunction); the REPL picks non-deterministically.
any { ... }
动作描述状态转换,返回值——返回表示动作触发。
booltruequint
action init: bool = all {
leader' = 0,
phase' = Idle,
votes' = Set(),
log' = List(),
state' = Map(),
}
action propose(node: int): bool = all {
phase == Idle,
node > 0,
leader' = node,
phase' = Propose,
votes' = votes,
log' = log,
state' = state,
}核心规则:
- 每个必须在每个动作中赋值(使用
var保持不变)。x' = x - ——所有子表达式必须成立(合取)。守卫是
all { ... }内的普通布尔表达式。all { } - ——至少有一个表达式成立(析取);REPL会非确定性选择。
any { ... }
Non-determinism
非确定性
quint
action step: bool = any {
propose(1),
propose(2),
vote,
timeout,
}
// Non-deterministic choice from a set
action deliverMessage: bool = {
nondet msg = pending.oneOf()
all {
pending.size() > 0,
delivered' = delivered.union(Set(msg)),
pending' = pending.exclude(Set(msg)),
// ... other vars unchanged
}
}quint
action step: bool = any {
propose(1),
propose(2),
vote,
timeout,
}
// 从集合中非确定性选择
action deliverMessage: bool = {
nondet msg = pending.oneOf()
all {
pending.size() > 0,
delivered' = delivered.union(Set(msg)),
pending' = pending.exclude(Set(msg)),
// ... 其他变量保持不变
}
}Set operators
集合运算符
quint
Set(1, 2, 3).contains(2) // true
Set(1, 2).union(Set(2, 3)) // Set(1, 2, 3)
Set(1, 2, 3).intersect(Set(2, 3)) // Set(2, 3)
Set(1, 2, 3).exclude(Set(2)) // Set(1, 3)
Set(1, 2, 3).filter(x => x > 1) // Set(2, 3)
Set(1, 2, 3).map(x => x * 2) // Set(2, 4, 6)
Set(1, 2, 3).fold(0, (acc, x) => acc + x) // 6
Set(1, 2, 3).size() // 3
Set(1, 2, 3).forall(x => x > 0) // true
Set(1, 2, 3).exists(x => x > 2) // true
1.to(5) // Set(1, 2, 3, 4, 5)
nondet x = Set(1, 2, 3).oneOf() // non-deterministic pick — only valid in nondet bindingsquint
Set(1, 2, 3).contains(2) // true
Set(1, 2).union(Set(2, 3)) // Set(1, 2, 3)
Set(1, 2, 3).intersect(Set(2, 3)) // Set(2, 3)
Set(1, 2, 3).exclude(Set(2)) // Set(1, 3)
Set(1, 2, 3).filter(x => x > 1) // Set(2, 3)
Set(1, 2, 3).map(x => x * 2) // Set(2, 4, 6)
Set(1, 2, 3).fold(0, (acc, x) => acc + x) // 6
Set(1, 2, 3).size() // 3
Set(1, 2, 3).forall(x => x > 0) // true
Set(1, 2, 3).exists(x => x > 2) // true
1.to(5) // Set(1, 2, 3, 4, 5)
nondet x = Set(1, 2, 3).oneOf() // 非确定性选择——仅在nondet绑定中有效List operators
列表运算符
quint
List(1, 2, 3).head() // 1
List(1, 2, 3).tail() // List(2, 3)
List(1, 2, 3).length() // 3
List(1, 2, 3).nth(1) // 2 (0-indexed)
List(1, 2, 3).append(4) // List(1, 2, 3, 4)
List(1, 2).concat(List(3, 4)) // List(1, 2, 3, 4)
List(1, 2, 3).foldl(0, (acc, x) => acc + x) // 6
List(1, 2, 3).select(x => x > 1) // List(2, 3)quint
List(1, 2, 3).head() // 1
List(1, 2, 3).tail() // List(2, 3)
List(1, 2, 3).length() // 3
List(1, 2, 3).nth(1) // 2 (0索引)
List(1, 2, 3).append(4) // List(1, 2, 3, 4)
List(1, 2).concat(List(3, 4)) // List(1, 2, 3, 4)
List(1, 2, 3).foldl(0, (acc, x) => acc + x) // 6
List(1, 2, 3).select(x => x > 1) // List(2, 3)Map operators
映射运算符
quint
Map("a" -> 1, "b" -> 2).get("a") // 1
Map("a" -> 1).put("b", 2) // Map("a" -> 1, "b" -> 2)
Map("a" -> 1, "b" -> 2).keys() // Set("a", "b")
Set(1, 2, 3).mapBy(k => k * 2) // Map(1 -> 2, 2 -> 4, 3 -> 6) — set of keys → mapquint
Map("a" -> 1, "b" -> 2).get("a") // 1
Map("a" -> 1).put("b", 2) // Map("a" -> 1, "b" -> 2)
Map("a" -> 1, "b" -> 2).keys() // Set("a", "b")
Set(1, 2, 3).mapBy(k => k * 2) // Map(1 -> 2, 2 -> 4, 3 -> 6) —— 键集合→映射Records
记录
Records group related fields into a named type. They are the primary tool for modelling structured state in Quint.
记录将相关字段分组为命名类型,是Quint中建模结构化状态的主要工具。
Type aliases for records
记录的类型别名
quint
type NodeState = {
phase: Phase, // enum: Idle | Propose | Vote | Commit
voted: bool,
log: List[int],
}
type Message = {
from: int,
to: int,
round: int,
payload: str,
}quint
type NodeState = {
phase: Phase, // 枚举:Idle | Propose | Vote | Commit
voted: bool,
log: List[int],
}
type Message = {
from: int,
to: int,
round: int,
payload: str,
}Creating and accessing
创建与访问
quint
val n: NodeState = { phase: Idle, voted: false, log: List() }
n.phase // Idle
n.voted // falsequint
val n: NodeState = { phase: Idle, voted: false, log: List() }
n.phase // Idle
n.voted // falseUpdating (immutable — returns a new record)
更新(不可变——返回新记录)
quint
{ ...n, phase: Propose } // ✅ preferred — idiomatic, handles multiple fields
{ ...n, voted: true, phase: Vote } // ✅ multiple fields at once
n.with("phase", Propose) // ⚠️ valid but non-idiomatic — field name is a string literalquint
{ ...n, phase: Propose } // ✅ 推荐——符合惯用写法,支持多字段更新
{ ...n, voted: true, phase: Vote } // ✅ 同时更新多个字段
n.with("phase", Propose) // ⚠️ 合法但不符合惯用写法——字段名为字符串字面量Records as state — when to group variables
作为状态的记录——何时分组变量
TLA+ specs typically flatten all state into independent top-level variables. Quint's type system lets you group them. When fields describe one cohesive local state, make a record type and use a single state variable of that type.
Group into a record when:
- They represent the local state of a single actor (e.g. one node's phase + log + vote)
- They are always passed together as function arguments
- An invariant relates multiple fields of the same conceptual entity
Keep flat when:
- The variables represent distinct concerns that change independently
- The component is simple and grouping adds no clarity
- The variables are intentionally in different ownership/lifecycle domains
TLA+规范通常将所有状态扁平化为独立的顶层变量。Quint的类型系统允许将变量分组。当字段描述一个内聚的本地状态时,创建记录类型并使用该类型的单个状态变量。
适合分组为记录的场景:
- 它们代表单个参与者的本地状态(例如一个节点的阶段+日志+投票)
- 它们总是作为函数参数一起传递
- 某个不变量涉及同一概念实体的多个字段
适合保持扁平的场景:
- 变量代表独立变化的不同关注点
- 组件简单,分组不会增加清晰度
- 变量属于不同的所有权/生命周期域
Example: preferred grouped local state vs. anti-pattern
示例:推荐的分组本地状态 vs 反模式
Preferred (cohesive local state):
quint
type LocalState = {
id: int,
phase: Phase,
est1: int,
est2: Option[int], // Option is from basicSpells, not built in — see Basic spells below
round: int,
crashed: bool,
leader: int,
received_messages: Set[Message],
}
var localState: LocalStateAvoid for cohesive local state:
quint
var id: int
var phase: Phase
var est1: int
var est2: Option[int]
var round: int
var crashed: bool
var leader: int
var received_messages: Set[Message]For N actors, use a map of grouped records:
quint
type LocalState = { phase: Phase, votedFor: int, log: List[int] }
var nodes: int -> LocalState
action commit(id: int): bool = {
val node = nodes.get(id)
all {
node.phase == Vote,
nodes' = nodes.put(id, {...node, phase: Commit}),
}
}推荐(内聚本地状态):
quint
type LocalState = {
id: int,
phase: Phase,
est1: int,
est2: Option[int], // Option来自basicSpells,非内置——见下方基础工具集
round: int,
crashed: bool,
leader: int,
received_messages: Set[Message],
}
var localState: LocalState内聚本地状态应避免的写法:
quint
var id: int
var phase: Phase
var est1: int
var est2: Option[int]
var round: int
var crashed: bool
var leader: int
var received_messages: Set[Message]对于N个参与者,使用分组记录的映射:
quint
type LocalState = { phase: Phase, votedFor: int, log: List[int] }
var nodes: int -> LocalState
action commit(id: int): bool = {
val node = nodes.get(id)
all {
node.phase == Vote,
nodes' = nodes.put(id, {...node, phase: Commit}),
}
}Nested records
嵌套记录
quint
type ClusterState = {
nodes: int -> NodeState,
leader: int,
epoch: int,
}
var cluster: ClusterState
// Read nested field:
cluster.nodes.get(1).phase
// Update nested field (must rebuild from the inside out):
val updated = {...cluster.nodes.get(1), phase: Commit}
cluster' = {...cluster, nodes: cluster.nodes.put(1, updated)}quint
type ClusterState = {
nodes: int -> NodeState,
leader: int,
epoch: int,
}
var cluster: ClusterState
// 读取嵌套字段:
cluster.nodes.get(1).phase
// 更新嵌套字段(必须从内到外重建):
val updated = {...cluster.nodes.get(1), phase: Commit}
cluster' = {...cluster, nodes: cluster.nodes.put(1, updated)}Records in sets (messages, events)
集合中的记录(消息、事件)
quint
var inFlight: Set[Message]
action send(src: int, dst: int, r: int, p: str): bool = all {
inFlight' = inFlight.union(Set({ from: src, to: dst, round: r, payload: p })),
// ...
}
// Filter by field:
inFlight.filter(m => m.to == nodeId)
inFlight.exists(m => m.round == currentRound and m.payload == "vote")quint
var inFlight: Set[Message]
action send(src: int, dst: int, r: int, p: str): bool = all {
inFlight' = inFlight.union(Set({ from: src, to: dst, round: r, payload: p })),
// ...
}
// 按字段过滤:
inFlight.filter(m => m.to == nodeId)
inFlight.exists(m => m.round == currentRound and m.payload == "vote")Sum types
求和类型
Sum types (variants) represent a value that can be one of several distinct cases.
quint
type Action =
| Propose({ value: int, proposer: int })
| Vote({ value: int, voter: int })
| Decide({ value: int })Each variant has a named constructor and carries one payload. A constructor takes exactly one argument — wrap multiple fields in a record (as above) or a tuple.
Construct a value by calling the constructor:
quint
val a: Action = Propose({ value: 1, proposer: 2 })Pattern-match with , binding the payload:
matchquint
pure def describeAction(a: Action): str =
match a {
| Propose(p) => "proposal"
| Vote(v) => "vote"
| Decide(d) => "decision"
}Use to ignore the payload when you only care which variant it is:
_quint
match a {
| Propose(_) => "proposal"
| _ => "other"
}Use sum types when a message, event, or state can take structurally different forms — not just different values of the same type.
求和类型(变体)表示可以是几种不同情况之一的值。
quint
type Action =
| Propose({ value: int, proposer: int })
| Vote({ value: int, voter: int })
| Decide({ value: int })每个变体都有一个命名构造函数,并携带一个负载。构造函数恰好接受一个参数——将多个字段包装在记录(如上)或元组中。
调用构造函数创建值:
quint
val a: Action = Propose({ value: 1, proposer: 2 })使用进行模式匹配,绑定负载:
matchquint
pure def describeAction(a: Action): str =
match a {
| Propose(p) => "proposal"
| Vote(v) => "vote"
| Decide(d) => "decision"
}当仅关心变体类型而忽略负载时,使用:
_quint
match a {
| Propose(_) => "proposal"
| _ => "other"
}当消息、事件或状态可以采用结构不同的形式(而不仅仅是同一类型的不同值)时,使用求和类型。
Enum types
枚举类型
Enum types are a special case of sum types where each case has no additional data.
quint
type Phase = Idle | Propose | Vote | Commit
var phase: Phase
if (phase == Propose) { ... }枚举类型是求和类型的特殊情况,每个情况没有额外数据。
quint
type Phase = Idle | Propose | Vote | Commit
var phase: Phase
if (phase == Propose) { ... }Variable grouping — decision guide
变量分组决策指南
Before writing declarations, answer these questions for each candidate group:
var| Question | Group → record if... | Keep flat if... |
|---|---|---|
| Do these vars always change together? | Yes, in most actions | No, they're independent |
| Do they describe the same entity? | Same node / same message / same round | Different concerns |
| Is there one instance or N instances? | Either one or N (group if cohesive; for N use | Flat only when concerns are truly independent |
| Do invariants relate them? | Invariant spans multiple fields of one entity | Invariant uses vars independently |
编写声明前,针对每个候选分组回答以下问题:
var| 问题 | 分组为记录的情况... | 保持扁平的情况... |
|---|---|---|
| 这些变量是否总是一起变化? | 是,在大多数动作中 | 否,它们相互独立 |
| 它们是否描述同一实体? | 同一节点/同一消息/同一轮次 | 不同关注点 |
| 是单个实例还是N个实例? | 单个或N个(内聚则分组;N个实例使用 | 仅当关注点真正独立时保持扁平 |
| 不变量是否关联它们? | 不变量涉及同一实体的多个字段 | 不变量独立使用变量 |
Boolean operators
布尔运算符
quint
not(p) // negation — Quint has no ! operator
p and q // conjunction
p or q // disjunction
p implies q // p => q (not(p) or q)
p iff q // p == q for booleans
and { p1, p2, p3 } // block form — equivalent to p1 and p2 and p3
or { p1, p2, p3 } // block form — at least one must holdand { }or { }all { }any { }quint
not(p) // 否定——Quint没有!运算符
p and q // 合取
p or q // 析取
p implies q // p => q (not(p) or q)
p iff q // 布尔值的等价性
and { p1, p2, p3 } // 块形式——等价于p1 and p2 and p3
or { p1, p2, p3 } // 块形式——至少一个必须成立and { }or { }all { }any { }Invariants and temporal properties
不变量与时态属性
quint
// Safety invariant — must hold in every reachable state
// @invariant
val noDuplicateLeader: bool =
leaders.size() <= 1
// Temporal property — evaluated over traces
// @temporal
temporal eventualProgress: bool =
eventually(committed.size() > 0)
// Temporal operators
eventually(p) // p holds in some future state
always(p) // p holds in all future states
p.implies(q) // p => qquint
// 安全不变量——必须在所有可达状态中成立
// @invariant
val noDuplicateLeader: bool =
leaders.size() <= 1
// 时态属性——在轨迹上评估
// @temporal
temporal eventualProgress: bool =
eventually(committed.size() > 0)
// 时态运算符
eventually(p) // p在某个未来状态成立
always(p) // p在所有未来状态成立
p.implies(q) // p => qAssume
Assume
quint
assume nodeCountPositive = N > 0
assume quorumMajority = 2 * quorum > NAn states a premise about constants, but it is not enforced — a violated
is silently ignored by , , and (none of them flags
it). It is documentation, not a checked constraint. To actually check a condition on
constants, write a test that asserts it (it executes and fails when the condition is
false):
assumeassumequint typecheckquint runquint verifyrunquint
run quorumAssumptionTest = all {
2 * quorum > N,
N > 0,
}Run it with ; the test fails (reporting which conjunct broke) if a constant
assignment violates the condition.
quint testquint
assume nodeCountPositive = N > 0
assume quorumMajority = 2 * quorum > Nassumeassumequint typecheckquint runquint verifyrunquint
run quorumAssumptionTest = all {
2 * quorum > N,
N > 0,
}使用运行它;如果常量赋值违反条件,测试会失败(报告哪个合取项不成立)。
quint testConditional and let
条件语句与let
quint
if (x > 0) "positive" else "non-positive"
val result = {
val doubled = x * 2
doubled + 1
}quint
if (x > 0) "positive" else "non-positive"
val result = {
val doubled = x * 2
doubled + 1
}REPL usage
REPL使用
Prefer CLI commands (, , , ) for all validation and execution tasks. Open the REPL ( or ) only when you need expression-level interaction the CLI does not provide.
quint typecheckquint runquint testquint verifyquintquint -r spec.qnt::ModuleNameType inspection:
>>> :type myExpression所有验证和执行任务优先使用CLI命令(、、、)。仅当需要CLI不支持的表达式级交互时,才打开REPL(或)。
quint typecheckquint runquint testquint verifyquintquint -r spec.qnt::ModuleName类型检查:
>>> :type myExpressionFile layout
文件布局
Split specs across two files:
<protocol-name>.qnt # main module — step, init, vars, invariants
<protocol-name>_test.qnt # test module — run tests and scenario witnesses (imports main)将规范拆分为两个文件:
<protocol-name>.qnt // 主模块——step、init、变量、不变量
<protocol-name>_test.qnt // 测试模块——运行测试和场景见证(导入主模块)Module responsibilities
模块职责
Main module ():
<protocol-name>.qnt- Declares all state variables, , actions, and safety invariants
init - must live in the main module — it is the entry point for
stepsimulationquint run - The module name matches the file stem: in
module myProtocolmyProtocol.qnt
Test module ():
<protocol-name>_test.qnt- Imports the main module ()
import myProtocol.* - Contains tests and scenario witnesses invoked via
runorquint testquint run - Inherits from the main module through the import
step
主模块():
<protocol-name>.qnt- 声明所有状态变量、、动作和安全不变量
init - 必须位于主模块中——它是
step模拟的入口点quint run - 模块名称与文件名匹配:中对应
myProtocol.qntmodule myProtocol
测试模块():
<protocol-name>_test.qnt- 导入主模块()
import myProtocol.* - 包含通过或
quint test调用的quint run测试和场景见证run - 通过导入继承主模块的
step
Which --main
to pass for quint run
--mainquint runquint run
应传入哪个--main
参数
quint run--mainquint run| Property location | Correct |
|---|---|
| Invariant defined in main module | main module name |
Witness / | test module name (it imports |
The primitive's field (set during indexing) always holds the correct value. Use it directly — do not derive from the filename.
module_namequint run| 属性位置 | 正确的 |
|---|---|
| 主模块中定义的不变量 | 主模块名称 |
测试模块中定义的见证/ | 测试模块名称(它从主模块导入 |
原语的字段(索引时设置)始终保存正确值。直接使用该值——不要从文件名推导。
module_nameBasic spells
基础工具集(basicSpells)
Many useful operators are not built into Quint but are available in , a standard library shipped with most Quint projects. Import it with:
basicSpells.qntquint
import basicSpells.* from "./basicSpells"Key definitions it provides:
| Definition | What it does |
|---|---|
| The option type — Quint has no built-in |
| The value inside |
| Blocks the action if |
| Set of all values in map |
| New map with |
| True if |
| |
| Copy of |
| Copy of set |
| First element of set / list satisfying |
| Max / min of two integers; absolute value |
When you see a spec using , , , or without an import, it is relying on basicSpells — check whether the project includes it. (Less common operators live in a sibling .)
OptionrequirevaluestransformValuesrareSpells.qnt许多实用运算符并非Quint内置,但可在中获取,这是随大多数Quint项目分发的标准库。通过以下方式导入:
basicSpells.qntquint
import basicSpells.* from "./basicSpells"它提供的核心定义:
| 定义 | 功能 |
|---|---|
| 可选类型——Quint没有内置 |
| |
| 如果 |
| 映射 |
| 将 |
| 如果 |
| 存在则返回 |
| 移除 |
| 移除/添加元素 |
| 集合/列表中第一个满足 |
| 两个整数的最大值/最小值;绝对值 |
当看到规范使用、、或但未导入时,它依赖于basicSpells——检查项目是否包含该库。(不太常用的运算符位于同级的中。)
OptionrequirevaluestransformValuesrareSpells.qntGuidelines
指南
Detailed references — read these when you need more than the quick reference above:
| File | Contents |
|---|---|
| Complete operator reference: extended set/list/map operators, |
| Witnesses vs invariants, result interpretation, progressive increase protocol, trace analysis, coverage standard |
| Hard language limitations: no string ops, no nested match, no destructuring, no loops, no early returns |
| Full CLI reference: |
| 14 core patterns: State Type, Pure Functions, Thin Actions, Map Pre-population, Syntax Rules, Undefined Behavior, Witnesses, Nondeterministic Testing, Separate Test Files, REPL-First Debugging, Separate Concerns First, Extract System Model, Types-First Scaffolding, Logic Stubs |
| Writing and debugging tests: |
| Choreo framework for distributed protocols: two-file split, |
详细参考资料——当需要超出上述快速参考的内容时阅读:
| 文件 | 内容 |
|---|---|
| 完整运算符参考:扩展的集合/列表/映射运算符、测试和见证用的 |
| 见证与不变量的对比、结果解读、渐进式协议、轨迹分析、覆盖标准 |
| 语言硬限制:无字符串操作、无嵌套match、无解构、无循环、无提前返回 |
| 完整CLI参考: |
| 14个核心模式:状态类型、纯函数、轻量动作、映射预填充、语法规则、未定义行为、见证、非确定性测试、分离测试文件、REPL优先调试、优先分离关注点、提取系统模型、类型优先脚手架、逻辑存根 |
| 测试编写与调试: |
| 分布式协议的Choreo框架:双文件拆分、 |