nim-typestates¶
Compile-time typestate validation for Nim.
What is this?¶
nim-typestates is a Nim library that enforces state machine patterns at compile time. Define your valid states and transitions, and the compiler ensures your code follows them.
import typestates
type
File = object
path: string
Closed = distinct File
Open = distinct File
typestate File:
states Closed, Open
transitions:
Closed -> Open
Open -> Closed
proc open(f: Closed): Open {.transition.} =
result = Open(f)
proc close(f: Open): Closed {.transition.} =
result = Closed(f)
# This works:
let f = Closed(File(path: "/tmp/test"))
let opened = f.open()
let closed = opened.close()
# This won't compile - Open can't transition to Open!
# let bad = opened.open()
The compiler verifies that your code follows the declared protocol. If it compiles, invalid state transitions are impossible. See Formal Guarantees for details.
Why typestates?¶
Traditional runtime state machines have problems:
- Runtime errors: Invalid transitions cause crashes or bugs
- Defensive code: You write
if state == Xchecks everywhere - Documentation drift: State diagrams don't match code
Typestates solve this by encoding states in the type system:
- Compile-time errors: Invalid transitions don't compile
- Self-documenting: Types show valid operations
- Zero runtime cost: It's just types
Key Features¶
- Compile-time validation — Invalid transitions fail at compile time with clear error messages
- Zero runtime cost — All validation happens during compilation, no runtime overhead
- Branching transitions —
Closed -> (Open | Errored) as OpenResultwith user-named branch types - Wildcard transitions —
* -> Closed(any state can transition to a specific state) - Generic typestates —
Container[T]with states likeEmpty[T],Full[T] - Cross-typestate bridges — Connect independent state machines with validated handoffs
- Generated helpers —
FileStateenum,FileStatesunion type, branch constructors - Visualization — Export to GraphViz DOT format for diagrams
State Machine Visualization¶
Generate diagrams directly from your typestate definitions:
Installation¶
Or add to your .nimble file:
Nim < 2.2.8 with Static Generics
If you use static generic 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 typestate
Regular generics (Container[T]) are not affected.
Quick Links¶
- Getting Started - Tutorial walkthrough
- DSL Reference - Complete syntax documentation
- Examples - Real-world patterns
- API Reference - Generated API docs
References¶
Foundational Papers¶
- Typestate: A Programming Language Concept (Strom & Yemini, 1986) - The original paper introducing typestates as a compile-time mechanism for tracking object state
- Typestates for Objects (Aldrich et al., 2009) - Extends typestates to object-oriented programming with practical implementation strategies
Tutorials and Introductions¶
- The Typestate Pattern in Rust - Accessible introduction to encoding typestates using Rust's type system
- Typestate Analysis (Wikipedia) - Overview of typestate analysis concepts and history
- Formal Verification (Wikipedia) - Background on formal methods that typestates relate to
Related Projects¶
- typestate crate for Rust - Procedural macro approach to typestates in Rust, similar design philosophy to nim-typestates
- Plaid Programming Language - Research language from CMU with first-class typestate support built into the language
License¶
MIT