DSL Reference
Copy MarkdownComplete reference for the defspec grammar. For the auto-generated entity listing with all options, see DSL-TLX.md.
Spec Structure
import TLX
defspec MySpec do
# Additional TLA+ modules (optional)
extends [:Sequences]
# Variables (state)
variable :name, default_value
# Constants (model parameters)
constant :name
# Custom initial constraints
initial do
constraint e(expression)
end
# Actions (state transitions)
action :name do
guard(e(condition)) # or: await(e(condition))
fairness :weak # or: :strong
next :var, value
next :var, e(expression)
next var1: val1, var2: val2 # batch form
# Non-deterministic branches
branch :name do
guard(e(condition))
next :var, value
end
# Non-deterministic pick from set
pick :var, :set do
next :other_var, e(var)
end
end
# Safety invariants
invariant :name, e(expression)
# Temporal properties
property :name, always(e(expression))
property :name, eventually(e(expression))
property :name, always(eventually(e(expression)))
property :name, leads_to(e(p), e(q))
# Concurrent processes
process :name do
set(:constant_name)
fairness :weak
variable :local_var, default
action :name do
# same as top-level actions
end
end
# Refinement
refines AbstractSpec do
mapping :abstract_var, e(concrete_expression)
end
endVariables
variable :x, 0 # integer default
variable :state, :idle # atom default
variable :flag, false # boolean default
variable :items, [1, 2] # list (TLA+ sequence) default
variable :x, type: :integer, default: 0 # explicit type (documentation only)
# Empty list default requires block syntax (Elixir treats [] as empty keyword opts):
variable :queue do
default []
endType annotations are for documentation — TLX doesn't enforce types. TLC uses the auto-generated type_ok invariant for type checking.
Constants
constant :max_retries
constant :nodesConstants are bound at model-checking time via the .cfg file or --model-values flag.
Actions
action :name do
guard(e(condition)) # boolean guard (alias: await)
fairness :weak # WF_vars(name) — or :strong for SF
next :var, value # set next-state value
endGuards: guard and await are interchangeable. Cannot use both on the same action.
Fairness: :weak means the action fires if it stays continuously enabled. :strong means it fires if it's enabled infinitely often.
Branches
action :provision do
guard(e(state == :pending))
branch :success do
next :state, :provisioned
end
branch :failure do
next :state, :failed
end
endTLC explores all branches exhaustively. Every branch must set all variables (or they become null in TLA+).
Pick (Non-Deterministic Choice)
action :serve do
pick :req, :requests do
next :current, e(req)
end
endEmits \E req \in requests : ... in TLA+ and with (req \in requests) { ... } in PlusCal.
Invariants
invariant :name, e(boolean_expression)Checked in every reachable state. Violation stops TLC with a counterexample trace.
Properties
property :name, always(e(p)) # []P
property :name, eventually(e(p)) # <>P
property :name, always(eventually(e(p))) # []<>P
property :name, leads_to(e(p), e(q)) # P ~> QTemporal properties are checked over infinite behaviors, not individual states.
Processes
process :worker do
set(:nodes)
fairness :weak
variable :local_state, :idle
action :work do
guard(e(local_state == :idle))
next :local_state, :working
end
endProcess-local variables are included in the global state space.
Refinement
refines AbstractSpec do
mapping :abstract_var, e(concrete_expression)
endGenerates TLA+ INSTANCE AbstractSpec WITH abstract_var <- concrete_expression. TLC checks that the concrete spec's behaviors satisfy the abstract spec's Spec formula.
Custom Init
initial do
constraint e(x >= 0 and x <= 10)
endConstraints are added to the auto-generated Init predicate alongside variable defaults.
Expression Functions
For the complete list of operators, functions, and patterns valid inside e(), see the Expression Reference.