TLA+ to TLX Mapping Reference

Copy Markdown

Comprehensive mapping of TLA+ concepts to their TLX DSL equivalents.

Importer coverage

Each table includes an Importer column describing how well TLX.Importer.TlaParser recovers the construct when reading TLA+ back into the TLX IR. ADR-0013 scopes the importer to lossless round-trip for TLX-emitted output and best-effort for hand-written TLA+.

Legend:

SymbolMeaning
Round-trips — emit → parse → emit preserves structure
partialStructural recognition only — body captured as an opaque string
Emit-only — parser has no rule; body falls to raw-string tier-2 fallback
Not applicable — emitter-only concern (automatic) or out of parser scope

Closing the partial / gap is the focus of sprints 54–59.

Module Structure

TLA+TLXImporterNotes
---- MODULE Name ----defspec Name doModule header
EXTENDS Integers, FiniteSetsAutomaticAlways included
EXTENDS Sequencesextends [:Sequences]Opt-in
VARIABLES x, yvariable :x, defaultNames recover; defaults not in TLA+
CONSTANTS Maxconstant :maxBound in .cfg model values
====endModule footer

State and Transitions

TLA+TLXImporterNotes
Init == x = 0 /\ y = 0variable :x, 0 + variable :y, 0partialInit recognized; body captured as raw string
Init == x = 0 /\ y \in {1, 2}initial do constraint(...) endpartialCustom Init; body not parsed to AST
Action == guard /\ x' = valaction :name do guard(e(...)); next :x, val endpartialAction shape recognized; guards/next raw
x' = x (UNCHANGED x)AutomaticUnmentioned variables stay unchanged
UNCHANGED << x, y >>AutomaticTLX emits UNCHANGED for you
Next == A1 \/ A2 \/ A3AutomaticDisjunction of all actions
Spec == Init /\ [][Next]_varsAutomaticGenerated with fairness
vars == << x, y >>AutomaticGenerated from variable declarations

Actions and Guards

TLA+TLXImporterNotes
/\ condition (guard)guard(e(condition)) or await(e(condition))Sprint 54: conjunct bodies parse to AST (foundation subset)
A == guard /\ x' = vaction :a do guard(e(...)); next :x, v endSprint 54: guard + transition RHS both parse to AST
A == P1 \/ P2 (disjunction)branch :p1 do ... end + branch :p2 do ... endDisjunction body captured as raw string
\E x \in S : action(x)pick :x, :s do ... endBody captured raw — Sprint 55

Processes (PlusCal)

PlusCalTLXImporterNotes
process (P \in S)process :p do set(:s); ... endpartialpluscal_parser.ex extracts structure; labels/bodies raw
fair processprocess :p, fairness: :weak do ... endpartialFairness keyword survives; body raw

Invariants and Properties

TLA+TLXImporterNotes
Inv == predicate (INVARIANT)invariant :inv, e(predicate)Sprint 54: body parses to AST (foundation subset)
Prop == []Pproperty :prop, always(e(p))Sprint 58: temporal operators round-trip
Prop == <>Pproperty :prop, eventually(e(p))Sprint 58
Prop == []<>Pproperty :prop, always(eventually(e(p)))Sprint 58 — nesting supported
Prop == P ~> Qproperty :prop, leads_to(e(p), e(q))Sprint 58
Prop == P \U Qproperty :prop, until(e(p), e(q))Sprint 58
Prop == P \W Qproperty :prop, weak_until(e(p), e(q))Sprint 58

Expressions

Importer note: Sprint 54 + 56 — all listed expression primitives round-trip as AST (), including extended arithmetic (\div, %, ^, unary -).

TLA+TLX inside e()Notes
x + 1, x - 1, x * yx + 1, x - 1, x * yArithmetic
x \div ydiv(x, y)Integer division
x % yrem(x, y)Modulo
x^yx ** yExponentiation
-x (unary)-xUnary negation
x = yx == yEquality
x # y or x /= yx != yInequality
x /\ yx and yConjunction
x \/ yx or yDisjunction
~xnot xNegation
IF c THEN a ELSE bif c, do: a, else: bConditional
p => qimplies(p, q)Implication
p <=> qequiv(p, q)Equivalence

Sets

Importer note: Sprint 55 shipped — set literals/comprehensions, \in/\subseteq/\union/\intersect/\ (difference), SUBSET, UNION, Cardinality, and a..b all round-trip as AST ().

TLA+TLX inside e()Notes
{a, b, c}set_of([a, b, c])Set literal
x \in Sin_set(x, s)Membership
S \union Tunion(s, t)Union
S \intersect Tintersect(s, t)Intersection
S \ Tdifference(s, t)Set difference
S \subseteq Tsubset(s, t)Subset
Cardinality(S)cardinality(s)Size
{x \in S : P}filter(:x, s, pred)Set comprehension (filter)
{expr : x \in S}set_map(:x, s, expr)Set image / map
SUBSET Spower_set(s)Power set
UNION Sdistributed_union(s)Flatten a set of sets
a..brange(a, b)Integer range
S \X Tcross(s, t)Cartesian product

Functions (Maps)

Importer note: Sprint 55 + 56 — f[x], DOMAIN f, EXCEPT (single + multi-key), records, function constructor ([x \in S |-> expr]), and function set ([S -> T]) all round-trip as AST (). LAMBDA remains until Sprint 57.

TLA+TLXNotes
f[x]at(f, x)Application
[f EXCEPT ![x] = v]except(f, x, v)Single-key update
[f EXCEPT ![k1]=v1, ![k2]=v2]except_many(f, [{k1,v1}, {k2,v2}])Multi-key update
DOMAIN fdomain(f)Keys
[a |-> 1, b |-> 2]record(a: 1, b: 2)Record construction
[x \in S |-> expr]fn_of(:x, s, expr)Function constructor
[S -> T]fn_set(s, t)Function set (emission-only, not simulator)
LAMBDA x: exprInside select_seq/3 onlyNo standalone constructor (Sprint 49 partial)

Sequences

Require extends [:Sequences].

Importer note: Sprint 57 — Len, Append, Head, Tail, SubSeq, \o, Seq, and SelectSeq all round-trip as AST (). LAMBDA is scoped to SelectSeq's second argument only; standalone LAMBDA remains per ADR-0013.

TLA+TLX inside e()Notes
Len(s)len(s)Length
Append(s, x)append(s, x)Append
Head(s)head(s)First element
Tail(s)tail(s)All but first
SubSeq(s, m, n)sub_seq(s, m, n)Subsequence
s \o tconcat(s, t)Concatenation
SelectSeq(s, Test)select_seq(:x, s, pred)Filter sequence (emits LAMBDA)
Seq(S)seq_set(s)Sequence set (type assertion, emission-only)

Tuples

Importer note: Sprint 56 — <<a, b, c>> round-trips as AST ().

TLA+TLXNotes
<<a, b, c>>tuple([a, b, c])Explicit tuple constructor
<<a, b, c>>[a, b, c]List literal as default variable value also emits as TLA+ sequence

Other Constructs

Importer note: CHOOSE (Sprint 55) and CASE (Sprint 58) both round-trip as AST (). LET/IN remains .

TLA+TLXNotes
CHOOSE x \in S : Pchoose(:x, s, pred)Deterministic selection
CASE p1 -> e1 [] p2 -> e2case_of([{cond, val}, ...])Multi-way conditional (explicit)
CASE ... [] OTHER -> d{:otherwise, d} in clauses, or case/do insidee()`OTHER fallback
LET x == expr IN bodylet_in(:x, binding, body)Local definition

Refinement

TLA+TLXImporterNotes
INSTANCE Abstract WITH var <- exprrefines Abstract do mapping :var, e(expr) endNot parsed back

Not Supported

See TLA+ constructs not supported by TLX for the full list.