Skip to main content
This guide covers Alloy syntax comprehensively. If you’re new to Alloy, start with Alloy Basics for a gentler introduction. This reference assumes you understand the core concepts: signatures, relations, facts, and the basic workflow. Alloy 6 introduced temporal operators for modeling state over time. This guide covers both classic (static) Alloy and the temporal extensions.

When to Use Alloy

Alloy excels at finding counterexamples to structural properties. Use it when you need to answer:
  • “Can this bad state exist?” - Alloy searches for violations
  • “Is this constraint sufficient?” - Alloy finds gaps
  • “What does this data model actually allow?” - Alloy generates instances
Alloy uses SAT-based bounded verification. It doesn’t prove properties hold for all possible sizes, it exhaustively checks within the bounds you specify (typically 3-5 atoms per signature). This is usually enough to find bugs; most flaws manifest in small counterexamples. For temporal properties like “this eventually happens” or protocol correctness over time, consider TLA+. The two tools complement each other well.

Signatures

Signatures define the types of atoms in your model. They’re like classes in OOP or tables in a database, but with relational semantics.

Basic Signatures

Signature Modifiers

The one, lone, and some modifiers constrain cardinality:

Signature Extension

Extensions create subtypes. Each Cat atom is also an Animal atom. Extensions are unique in the sense that no atom is both a Cat and a Dog.

Signature Subsets

The in keyword creates subsets that aren’t necessarily unique. An atom can be both an Employee and something else that extends Person.

Disjoint Subsets

To ensure subsets don’t overlap, add a disjointness fact. A Person can still be neither Student nor Faculty.

Fields and Multiplicity

Fields define relations between signatures. Every field is a relation, even “attributes” are relations to value atoms.

Field Multiplicity

Without a keyword, one is assumed for simple relations. For relations with arrow (->), set is the default.

Binary Relations

Most fields are binary relations (from this signature to another):

Ternary and Higher Relations

Relations can have higher arity:
To use: p.membership[org] returns the roles person p has in organization org.

Relation Constraints

Relational Operators

Alloy is fundamentally relational. Understanding these operators is essential.

Join (.)

The dot operator navigates relations:
Join composes relations. If r: A -> B and s: B -> C, then r.s: A -> C.

Union (+)

Combines sets or relations:

Intersection (&)

Elements in both:

Difference (-)

Elements in first but not second:

Transpose (~)

Reverses a relation:
If r: A -> B, then ~r: B -> A.

Transitive Closure (^)

One or more applications of a relation:
Transitive closure is powerful for reachability, ancestry, and graph analysis.

Reflexive Transitive Closure (*)

Zero or more applications (includes identity):
The difference: ^r requires at least one step; *r includes staying in place.

Product (->)

Creates pairs:

Domain Restriction (<:)

Limits a relation to a domain subset:
If r: A -> B and s: set A, then s <: r: A -> B contains only pairs where the first element is in s.

Range Restriction (:>)

Limits a relation to a range subset:
If r: A -> B and s: set B, then r :> s: A -> B contains only pairs where the second element is in s.

Override (++)

Replaces mappings:
For function-like relations, ++ updates entries. If key exists in oldState, the new value replaces it.

Cardinality (#)

Counts elements:

Quantifiers

Quantifiers express properties over sets.

Universal (all)

Every element satisfies the condition.

Existential (some)

At least one element satisfies the condition.

None (no)

No element satisfies the condition. Equivalent to not some.

Unique (one)

Exactly one element satisfies the condition.

At Most One (lone)

Zero or one element satisfies the condition.

Disjoint Quantification

The disj modifier ensures quantified variables are distinct.

Logical Operators

Combine quantified expressions:

Facts, Predicates, Functions, Assertions

These organize constraints and queries.

Facts

Facts are constraints that always hold. Alloy only considers instances satisfying all facts.
Facts constrain the search space. Use them for invariants that define your model.

Predicates

Predicates are named, parameterized formulas. They can be true or false.
Use predicates to define reusable conditions or as targets for run commands.

Functions

Functions return values (sets/relations). They’re expressions, not constraints.

Assertions

Assertions are properties you claim should hold. The check command searches for violations.
Unlike facts, assertions don’t constrain the model, they query it.

Comprehensions

Set comprehensions build sets from expressions.

Basic Comprehension

Expression Comprehension

In Predicates and Functions

Let Expressions

let binds names to expressions for clarity and reuse.
Let bindings are purely syntactic, they don’t affect semantics.

Commands

Commands tell the Analyzer what to do.

Run

The run command searches for instances.

Check

The check command searches for assertion violations.

Scope

Bounds limit the search space. By default, bounds mean “at most N atoms” (0 to N), not “exactly N.”
Use exactly when you need a specific count. Without it, Alloy explores all sizes up to the bound, which is usually what you want for finding counterexamples.

Expect

Expect indicates anticipated results (for documentation/testing).

Module System

Modules organize specifications into reusable units.

Basic Modules

Opening Modules

Private Declarations

Parameterized Modules

Usage:

Standard Library

Alloy includes utility modules:

Temporal Operators (Alloy 6)

Alloy 6 introduced temporal logic for modeling state over time.

Variable Fields

The var keyword marks mutable relations.

Priming

The prime operator (') refers to the next state:

Temporal Quantifiers

Traces and Liveness

Temporal Example: Mutex

Built-in Relations and Constants

Universal Set

Empty Set

Identity Relation

Integer Operations

Note: Alloy uses bounded integers. The default is 4-bit, giving a range of -8 to 7. Use for N Int to set bitwidth (e.g., for 5 Int gives 5-bit integers: -16 to 15). Integer overflow wraps silently, so increase bitwidth if your model uses larger numbers.

Common Patterns

State Machine

Graph Reachability

Ownership / Containment

Access Control

Ordered Elements

Complete Example: Key-Value Store

A specification combining multiple concepts:

Quick Reference

Signature Modifiers

Field Multiplicity

Relational Operators

Logical Operators

Quantifiers

Temporal Operators (Alloy 6)

Commands

Where to Go Next

This guide covered Alloy syntax comprehensively. For practical application:
  1. Alloy Basics — If you skipped it, the gentle introduction provides context
  2. MacAlloy — Native macOS tool for running specifications
  3. AD Security Model — Real-world Alloy for Active Directory security
  4. Software Abstractions — Daniel Jackson’s definitive book
  5. Alloy Documentation — Official reference
The best way to learn is to model something you care about. Start small, run frequently, and let the Analyzer show you what your model actually allows.