Compile-time state machine validation for Nim.
nim-typestates encodes state machines in Nim's type system. Invalid state transitions become compile-time errors instead of runtime bugs.
import typestates
type
Payment = object
id: string
amount: int
Created = distinct Payment
Authorized = distinct Payment
Captured = distinct Payment
typestate Payment:
states Created, Authorized, Captured
transitions:
Created -> Authorized
Authorized -> Captured
proc authorize(p: Created): Authorized {.transition.} =
result = Authorized(p.Payment)
proc capture(p: Authorized): Captured {.transition.} =
result = Captured(p.Payment)
# Valid usage
let payment = Created(Payment(id: "pay_123", amount: 9999))
let authed = payment.authorize()
let captured = authed.capture()
# Compile error: type mismatch, got 'Created' but expected 'Authorized'
# let bad = payment.capture()The typestate macro and {.transition.} pragma enforce state machine
rules at compile time. A program that compiles has been verified by the
compiler to contain no invalid state transitions.
- Protocol adherence: Operations are only callable in valid states
- Transition validity: All
{.transition.}procs follow declared paths - State exclusivity: Each object occupies exactly one state
- Functional correctness: The implementation of each proc
- Specification accuracy: Whether the declared state machine matches the intended real-world protocol
The compiler verifies that your code follows the declared protocol. It does not verify that the protocol itself is correct.
nimble install typestatesOr add to your .nimble file:
requires "typestates >= 0.1.0"
⚠️ Nim < 2.2.8 with Static GenericsIf you use
staticgeneric parameters (e.g.,Buffer[N: static int]) with ARC/ORC/AtomicARC, you may hit a Nim codegen bug fixed in Nim 2.2.8. The library detects this and shows workarounds. Options:
- Use
--mm:refcinstead of ARC/ORC- Make your base type inherit from
RootObjand addinheritsFromRootObj = true- Upgrade to Nim >= 2.2.8 (when released)
- Add
consumeOnTransition = falseto your typestateRegular generics (
Container[T]) are not affected.
import typestates
type
File = object
path: string
fd: int
Closed = distinct File
Open = distinct Filetypestate File:
states Closed, Open
transitions:
Closed -> Open
Open -> Closedproc open(f: Closed, path: string): Open {.transition.} =
var file = f.File
file.path = path
file.fd = 1
result = Open(file)
proc close(f: Open): Closed {.transition.} =
result = Closed(f.File)proc read(f: Open, n: int): string {.notATransition.} =
result = "data"
proc write(f: Open, data: string) {.notATransition.} =
discard| Feature | Description |
|---|---|
| Compile-time validation | Invalid transitions are compilation errors |
| Zero runtime cost | All validation happens at compile time |
| Branching transitions | Open -> (Closed | Error) as Result |
| Wildcard transitions | * -> Closed (any state can transition) |
| Generic typestates | Container[T] with states like Empty[T], Full[T] |
| Cross-type bridges | Transition between different typestates |
| Visualization | Export to GraphViz DOT, PNG, SVG |
| CLI tool | Project-wide verification |
| Strict mode | Require explicit marking of all state operations |
| Sealed typestates | Control external module access |
Model resource transformation and protocol handoff between typestates:
import typestates
import ./session
typestate AuthFlow:
states Pending, Authenticated, Failed
transitions:
Pending -> Authenticated
Pending -> Failed
bridges:
Authenticated -> Session.Active
Failed -> Session.Guest
# Bridge implementation
converter toSession(auth: Authenticated): Active {.transition.} =
Active(Session(userId: auth.AuthFlow.userId))Bridges are validated at compile time and shown in visualization.
Verify typestate rules across your project:
typestates verify src/Generate state machine diagrams from your code:
# Generate SVG
typestates dot src/ | dot -Tsvg -o states.svg
# Generate PNG
typestates dot src/ | dot -Tpng -o states.png
# Minimal output for custom styling
typestates dot --no-style src/ > states.dot- Getting Started
- DSL Reference
- Cross-Type Bridges
- Generic Typestates
- Formal Guarantees
- Error Handling
- Examples
- Strict Mode
- Verification
- CLI Reference
- Visualization
- API Reference
- Contributing
- Typestate Pattern in Rust
- typestate crate for Rust
- Plaid Language
- Typestate: A Programming Language Concept (Strom & Yemini, 1986)
MIT