Skip to content

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 == X checks 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 transitionsClosed -> (Open | Errored) as OpenResult with user-named branch types
  • Wildcard transitions* -> Closed (any state can transition to a specific state)
  • Generic typestatesContainer[T] with states like Empty[T], Full[T]
  • Cross-typestate bridges — Connect independent state machines with validated handoffs
  • Generated helpersFileState enum, FileStates union type, branch constructors
  • Visualization — Export to GraphViz DOT format for diagrams

State Machine Visualization

Generate diagrams directly from your typestate definitions:

typestates dot src/ | dot -Tsvg -o states.svg

State Machine Diagram

Installation

nimble install typestates

Or add to your .nimble file:

requires "typestates >= 0.1.0"

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:

  1. Use --mm:refc instead of ARC/ORC
  2. Make your base type inherit from RootObj and add inheritsFromRootObj = true
  3. Upgrade to Nim >= 2.2.8 (when released)
  4. Add consumeOnTransition = false to your typestate

Regular generics (Container[T]) are not affected.

References

Foundational Papers

Tutorials and Introductions

License

MIT