hsm 1.3.3
hsm: ^1.3.3 copied to clipboard
A Hierarchical State Machine (HSM) library for Dart implementing the Unified DSK (Domain Specific Kit).
Hierarchical State Machine (HSM) #
A high-performance, feature-rich hierarchical state machine library for Dart. Ported from the Unified Domain Specific Kit (DSK) specification, ensuring cross-language compatibility and predictable behavior.
Features #
- Hierarchical States: Support for nested states, enabling complex behavioral modeling with ease.
- O(1) Dispatch: Transition lookup tables are precomputed at build time, ensuring constant-time event dispatch regardless of hierarchy depth.
- Unified DSL: A declarative Domain Specific Language (DSL) that is consistent across JS, Go, Python, and Dart implementations.
- Rich Semantics: Full support for
entry,exit,guard,effect,initial,choice,defer, and more. - MUID: Integrated 64-bit Monotonically Unique ID generator for high-performance, sorting-friendly identifiers.
- Groups: Composite support for managing multiple state machine instances as a single logical unit.
Usage #
Define your state machine using the declarative DSL:
import 'package:hsm/hsm.dart';
void main() {
final model = define('TrafficLight', [
state('Operational', [
state('Green', [
transition([on('timer'), target('Yellow')])
]),
state('Yellow', [
transition([on('timer'), target('Red')])
]),
state('Red', [
transition([on('timer'), target('Green')])
]),
initial(target('Green'))
]),
initial(target('Operational'))
]);
final sm = HSM(Context(), Instance(), model).start();
print(sm.state); // /TrafficLight/Operational/Green
sm.dispatch(Event(name: 'timer'));
print(sm.state); // /TrafficLight/Operational/Yellow
}
Architecture #
- [Model]: The static definition of the state machine structure. Immutable at runtime.
- [HSM]: The runtime engine that manages state, transitions, and event processing for a specific [Instance].
- [Instance]: The data object that holds the state machine's state. Can be subclassed to add domain data.
- [Context]: A cancellation and lifecycle context manager.
Performance #
The engine is optimized for high-throughput scenarios (e.g., game loops, high-frequency trading, reactive UIs). Benchmarks show >1.5 Million transitions per second on standard hardware for simple scenarios.
Best Practices #
To ensure reliable, testable, and maintainable state machines, adhere to the following guidelines:
Temporal Logic: Use after and every #
Do not uses loops or sleeps in Activities.
Blocking operations or infinite loops inside activity handlers prevent the engine from processing other events and destroy testability.
- BAD:
activity((ctx, instance, event) async { while(!ctx.done) { await Future.delayed(Duration(seconds: 1)); instance.dispatch(Event(name: 'tick')); } }) - GOOD: Use declarative temporal primitives.
- Timed Transitions: Use
after(duration)for one-off delayed transitions. - Periodic Events: Use
every(duration)for recurring events.
- Timed Transitions: Use
Purity: Guards must be Pure #
Guards must have NO side effects.
The engine may check guards multiple times or speculatively. A guard should only return a bool based on the current Context, Instance, or Event.
- BAD:
guard((ctx, i, e) { i.count++; return i.count > 10; }) - GOOD:
guard((ctx, i, e) => i.count > 10)
Side Effects #
Encapsulate side effects in entry, exit, and effect.
- Entry: "I am now in this state, start X" (e.g., Start Timer, Connect Socket).
- Exit: "I am leaving this state, stop X" (e.g., Cancel Timer, Disconnect).
- Effect: "I am moving between states, do Y" (e.g., Log transition, Emit metric).
Never perform side effects in canHandle checks or purely logic-based helper functions.