Loading...
Loading...
Compare original and translation side by side
module MyProtocol {
// type aliases, constants, state, actions, properties
}import Voting.* // all definitions
import Voting(quorum) // specific definition
import Voting as V // namespace aliasmodule MyProtocol {
// 类型别名、常量、状态、动作、属性
}import Voting.* // 导入所有定义
import Voting(quorum) // 导入特定定义
import Voting as V // 命名空间别名| Type | Description | Example |
|---|---|---|
| Integers | |
| Booleans | |
| Strings | |
| Finite set | |
| Ordered sequence | |
| Key-value map (type is | value: |
| Tuple | |
| Record | |
| Sum (variant) | (use type alias) |
type NodeId = int
type Phase = Idle | Propose | Vote | Commit // enum — prefer over string literals| 类型 | 描述 | 示例 |
|---|---|---|
| 整数 | |
| 布尔值 | |
| 字符串 | |
| 有限集合 | |
| 有序序列 | |
| 键值映射(类型为 | 值: |
| 元组 | |
| 记录 | |
| 求和类型(变体) | (使用类型别名) |
type NodeId = int
type Phase = Idle | Propose | Vote | Commit // 枚举——优先于字符串字面量// 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 valdefvaldefval// 模块参数——实例化时固定,不是状态变量
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 valdefvaldefvaltype 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)valpure deftype LocalState = {
leader: int,
phase: Phase, // 枚举(见类型别名)——优先于裸字符串
votes: Set[int],
log: List[str],
}
var localState: LocalState // 内聚的本地协议状态
var peers: int -> str // 独立关注点(节点元数据)valpure defbooltrueaction 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,
}varx' = xall { ... }all { }any { ... }booltrueaction 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,
}varx' = xall { ... }all { }any { ... }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
}
}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(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 bindingsSet(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(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)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("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 → mapMap("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) —— 键集合→映射type NodeState = {
phase: Phase, // enum: Idle | Propose | Vote | Commit
voted: bool,
log: List[int],
}
type Message = {
from: int,
to: int,
round: int,
payload: str,
}type NodeState = {
phase: Phase, // 枚举:Idle | Propose | Vote | Commit
voted: bool,
log: List[int],
}
type Message = {
from: int,
to: int,
round: int,
payload: str,
}val n: NodeState = { phase: Idle, voted: false, log: List() }
n.phase // Idle
n.voted // falseval n: NodeState = { phase: Idle, voted: false, log: List() }
n.phase // Idle
n.voted // false{ ...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 literal{ ...n, phase: Propose } // ✅ 推荐——符合惯用写法,支持多字段更新
{ ...n, voted: true, phase: Vote } // ✅ 同时更新多个字段
n.with("phase", Propose) // ⚠️ 合法但不符合惯用写法——字段名为字符串字面量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: LocalStatevar 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]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}),
}
}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: LocalStatevar 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]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}),
}
}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)}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)}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")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")type Action =
| Propose({ value: int, proposer: int })
| Vote({ value: int, voter: int })
| Decide({ value: int })val a: Action = Propose({ value: 1, proposer: 2 })matchpure def describeAction(a: Action): str =
match a {
| Propose(p) => "proposal"
| Vote(v) => "vote"
| Decide(d) => "decision"
}_match a {
| Propose(_) => "proposal"
| _ => "other"
}type Action =
| Propose({ value: int, proposer: int })
| Vote({ value: int, voter: int })
| Decide({ value: int })val a: Action = Propose({ value: 1, proposer: 2 })matchpure def describeAction(a: Action): str =
match a {
| Propose(p) => "proposal"
| Vote(v) => "vote"
| Decide(d) => "decision"
}_match a {
| Propose(_) => "proposal"
| _ => "other"
}type Phase = Idle | Propose | Vote | Commit
var phase: Phase
if (phase == Propose) { ... }type Phase = Idle | Propose | Vote | Commit
var phase: Phase
if (phase == Propose) { ... }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个实例使用 | 仅当关注点真正独立时保持扁平 |
| 不变量是否关联它们? | 不变量涉及同一实体的多个字段 | 不变量独立使用变量 |
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 { }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 { }// 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 => q// 安全不变量——必须在所有可达状态中成立
// @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 nodeCountPositive = N > 0
assume quorumMajority = 2 * quorum > Nassumeassumequint typecheckquint runquint verifyrunrun quorumAssumptionTest = all {
2 * quorum > N,
N > 0,
}quint testassume nodeCountPositive = N > 0
assume quorumMajority = 2 * quorum > Nassumeassumequint typecheckquint runquint verifyrunrun quorumAssumptionTest = all {
2 * quorum > N,
N > 0,
}quint testif (x > 0) "positive" else "non-positive"
val result = {
val doubled = x * 2
doubled + 1
}if (x > 0) "positive" else "non-positive"
val result = {
val doubled = x * 2
doubled + 1
}quint typecheckquint runquint testquint verifyquintquint -r spec.qnt::ModuleName>>> :type myExpressionquint typecheckquint runquint testquint verifyquintquint -r spec.qnt::ModuleName>>> :type myExpression<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 // 测试模块——运行测试和场景见证(导入主模块)<protocol-name>.qntinitstepquint runmodule myProtocolmyProtocol.qnt<protocol-name>_test.qntimport myProtocol.*runquint testquint runstep<protocol-name>.qntinitstepquint runmyProtocol.qntmodule myProtocol<protocol-name>_test.qntimport myProtocol.*quint testquint runrunstep--mainquint runquint run--mainquint run| Property location | Correct |
|---|---|
| Invariant defined in main module | main module name |
Witness / | test module name (it imports |
module_namequint run| 属性位置 | 正确的 |
|---|---|
| 主模块中定义的不变量 | 主模块名称 |
测试模块中定义的见证/ | 测试模块名称(它从主模块导入 |
module_namebasicSpells.qntimport basicSpells.* from "./basicSpells"| 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 |
OptionrequirevaluestransformValuesrareSpells.qntbasicSpells.qntimport basicSpells.* from "./basicSpells"| 定义 | 功能 |
|---|---|
| 可选类型——Quint没有内置 |
| |
| 如果 |
| 映射 |
| 将 |
| 如果 |
| 存在则返回 |
| 移除 |
| 移除/添加元素 |
| 集合/列表中第一个满足 |
| 两个整数的最大值/最小值;绝对值 |
OptionrequirevaluestransformValuesrareSpells.qnt| 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框架:双文件拆分、 |