A lightweight, type-safe statechart implementation in Go using generics. Supports hierarchical states, parallel states, history states, guards, and actions in 272 lines of code.
- Generic Type Safety: Use any comparable type for states and events, any type for context
- Hierarchical States: Nested states with behavioral inheritance
- Parallel States: Multiple concurrent state regions
- History States: Remember last active child state
- Guards and Actions: Conditional transitions with side effects
- Thread Safe: Concurrent access protection with RWMutex
- Compile-time Definitions: Define state structure at compile time
- Runtime Flexibility: Dynamic event processing and state transitions
- State IDs (
S comparable): Any comparable type (string, int, custom types) - Event Types (
E comparable): Any comparable type for events - Context Data (
C any): Any type for user-defined context/extended state
- Type Safety: Compile-time checking prevents invalid state/event combinations
- Performance: No reflection or type assertions needed
- Reusability: Same library works with different type combinations
- Zero Allocation: Struct-based generics avoid interface boxing
The statechart runtime is the execution engine that orchestrates state machine behavior:
- State Management: Tracks current active state and maintains hierarchical relationships
- Event Processing: Receives events and finds applicable transitions based on current state
- Transition Execution: Coordinates exit actions, transition actions, and entry actions
- Context Preservation: Maintains user-defined state data across transitions
- Hierarchy Navigation: Supports behavioral inheritance where child states inherit parent transitions
- Concurrency Control: Thread-safe operations for multi-goroutine environments
The runtime fits in ~270 LOC by focusing on essential statechart semantics:
- Compile-time Structure: States and transitions defined at compile time
- Runtime Execution: Dynamic event processing and state changes
- Memory Efficient: Direct struct allocation, no object pools needed for this size
- Type Safe: Generics eliminate runtime type checking
package main
import (
"context"
"github.com/user/statechart"
)
// Define your types
type MyState string
type MyEvent string
type MyContext struct {
Counter int
}
func main() {
// Create runtime with initial context
ctx := statechart.Context[MyContext](MyContext{Counter: 0})
runtime := statechart.NewRuntime[MyState, MyEvent, MyContext](ctx)
// Add states
runtime.AddState(
statechart.StateID[MyState]("idle"),
statechart.SimpleState,
statechart.StateID[MyState](""),
)
// Add transitions with guards and actions
runtime.AddTransition(
statechart.StateID[MyState]("idle"),
statechart.StateID[MyState]("active"),
statechart.EventType[MyEvent]("start"),
func(ctx context.Context, from statechart.StateID[MyState], event statechart.EventType[MyEvent], context *statechart.Context[MyContext]) bool {
return (*context).Counter < 10 // Guard condition
},
func(ctx context.Context, from, to statechart.StateID[MyState], event statechart.EventType[MyEvent], context *statechart.Context[MyContext]) error {
(*context).Counter++
return nil
},
)
// Start and use
runtime.SetInitialState(statechart.StateID[MyState]("idle"))
runtime.Start(context.Background())
runtime.SendEvent(context.Background(), statechart.EventType[MyEvent]("start"))
}Runtime[S, E, C]: Main statechart runtime engineState[S, E, C]: Individual state with hierarchy and actionsTransition[S, E, C]: State transition with guard and actionGuard[S, E, C]: Function type for transition conditionsAction[S, E, C]: Function type for side effects
SimpleState: Basic state with no childrenCompositeState: Hierarchical state with child statesParallelState: Concurrent state regionsHistoryState: Remembers last active child state
AddState(): Define states with hierarchyAddTransition(): Define transitions with guards/actionsSendEvent(): Process eventsIsInState(): Check current state (including ancestors)GetContext(): Access user context data
See example/main.go for a complete door controller example demonstrating:
- Hierarchical states (Closed -> Locked/Unlocked)
- Guards (can't open when locked)
- Actions (logging, context updates)
- State introspection
- Context management
go get github.com/user/statechartMIT License - see LICENSE file for details.