No description
Find a file
2025-12-21 04:25:28 -06:00
.github/workflows chore: add pre-commit with nph, custom code font, CI lint job 2025-12-20 21:46:32 -06:00
docs chore: add pre-commit with nph, custom code font, CI lint job 2025-12-20 21:46:32 -06:00
examples chore: bump minimum Nim version to 2.2.0 2025-12-06 23:21:00 -06:00
hooks fix(hooks): prevent infinite rebuild loop by checking file timestamps 2025-12-05 04:12:19 -06:00
scripts docs(visualization): add multi-typestate example with bridges diagram 2025-12-05 02:53:26 -06:00
src chore: add pre-commit with nph, custom code font, CI lint job 2025-12-20 21:46:32 -06:00
tests chore: add pre-commit with nph, custom code font, CI lint job 2025-12-20 21:46:32 -06:00
.gitignore feat: add constraint inference for generic typestates 2025-12-12 04:37:23 -06:00
.pre-commit-config.yaml chore: add pre-commit with nph, custom code font, CI lint job 2025-12-20 21:46:32 -06:00
CHANGELOG.md fix(cli): handle generic typestates in parser and DOT output 2025-12-12 19:35:08 -06:00
config.nims fix: only add single path in config.nims to avoid duplicate modules 2025-12-03 04:44:18 -06:00
CONTRIBUTING.md docs: add Contributing page, refine visualization styling 2025-12-05 12:53:06 -06:00
docs-requirements.txt chore: update mkdocstrings-nim to v0.2.1 2025-12-21 04:25:28 -06:00
LICENSE feat: implement compile-time typestate validation DSL for Nim 2025-11-29 15:20:40 -06:00
mkdocs.yml chore: add pre-commit with nph, custom code font, CI lint job 2025-12-20 21:46:32 -06:00
nim.cfg feat: add constraint inference for generic typestates 2025-12-12 04:37:23 -06:00
README.md fix: reorder workarounds (refc first, RootObj second, upgrade third with 'when released') 2025-12-09 14:01:12 -06:00
typestates.nimble fix(cli): handle generic typestates in parser and DOT output 2025-12-12 19:35:08 -06:00

nim-typestates

CI Docs Release License Nim

Compile-time state machine validation for Nim.

Overview

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()

Guarantees

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.

Verified at compile time

  • 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

Not verified

  • 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.

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.

Usage

Define states as distinct types

import typestates

type
  File = object
    path: string
    fd: int
  Closed = distinct File
  Open = distinct File

Declare valid transitions

typestate File:
  states Closed, Open
  transitions:
    Closed -> Open
    Open -> Closed

Implement transitions

proc 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)

Mark non-transitions

proc read(f: Open, n: int): string {.notATransition.} =
  result = "data"

proc write(f: Open, data: string) {.notATransition.} =
  discard

Key Features

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

Cross-Type Bridges

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.

CLI Tool

Verify typestate rules across your project:

typestates verify src/

Visualization

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

State Machine Visualization

Documentation

References

License

MIT