# Mindset
[](https://crates.io/crates/mindset)
[](https://crates.io/crates/mindset)
[](https://github.com/iepathos/mindset/actions/workflows/ci.yml)
[](LICENSE)
A zero-cost, effect-based state machine library for Rust.
## Overview
Mindset provides a flexible and type-safe state machine implementation that separates pure guard logic from effectful actions. Built on Stillwater's effect system, it enables you to write state machines that are:
- **Zero-cost by default**: No runtime overhead when effects aren't needed
- **Explicitly effectful**: Side effects are opt-in and clearly marked
- **Highly testable**: Pure guards and dependency injection via environment traits
- **Type-safe**: Compile-time guarantees about state transitions
## Features
- **Pure Guard Functions**: Deterministic state validation with no side effects
- **Effectful Actions**: Explicit I/O and side effects when needed
- **Environment Pattern**: Clean dependency injection for testing
- **Zero-Cost Abstractions**: Pay only for what you use
- **Composable Effects**: Build complex behavior from simple trait combinations
## Quick Start
### Simple State Machine (Zero Cost)
```rust
use mindset::{StateMachine, State};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ConnectionState {
Disconnected,
Connecting,
Connected,
}
impl State for ConnectionState {}
fn main() {
use mindset::builder::{StateMachineBuilder, TransitionBuilder};
let machine = StateMachineBuilder::new()
.initial(ConnectionState::Disconnected)
.add_transition(
TransitionBuilder::new()
.from(ConnectionState::Disconnected)
.to(ConnectionState::Connecting)
.succeeds()
.build()
.unwrap()
)
.add_transition(
TransitionBuilder::new()
.from(ConnectionState::Connecting)
.to(ConnectionState::Connected)
.succeeds()
.build()
.unwrap()
)
.build()
.unwrap();
// Zero-cost transitions - compiles to direct state updates
}
```
### Effectful State Machine
```rust
use mindset::{StateMachine, State};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum OrderState {
Draft,
Submitted,
Processing,
Completed,
}
impl State for OrderState {}
struct Order {
id: u64,
total: f64,
}
// Define environment capabilities as traits
trait PaymentProcessor {
fn charge(&mut self, amount: f64) -> Result<(), String>;
}
trait Logger {
fn log(&mut self, message: &str);
}
// Pure guard - no side effects
fn can_submit(order: &Order) -> bool {
order.total > 0.0
}
// Effectful action - explicit environment usage
fn submit_order<Env>(order: &mut Order, env: &mut Env) -> Result<(), String>
where
Env: PaymentProcessor + Logger,
{
env.log(&format!("Submitting order {}", order.id));
env.charge(order.total)?;
Ok(())
}
fn main() {
use mindset::builder::{StateMachineBuilder, TransitionBuilder};
let machine = StateMachineBuilder::new()
.initial(OrderState::Draft)
.add_transition(
TransitionBuilder::new()
.from(OrderState::Draft)
.to(OrderState::Submitted)
.succeeds()
.build()
.unwrap()
)
.add_transition(
TransitionBuilder::new()
.from(OrderState::Submitted)
.to(OrderState::Processing)
.succeeds()
.build()
.unwrap()
)
.build()
.unwrap();
// For custom effectful actions, see the Builder Guide
}
```
## Architecture
### Core Design Principles
1. **Pure Guards, Effectful Actions**
- Guards are pure functions that validate state transitions
- Actions perform side effects and state changes
- Clear separation enables testing and reasoning
2. **Zero-Cost by Default**
- No effects means no runtime overhead
- Effects are opt-in via explicit environment parameters
- Compiler optimizes away unused abstractions
3. **Environment Pattern**
- Dependencies expressed as trait bounds
- Compose environments from multiple traits
- Easy mocking for tests
4. **Explicit Over Implicit**
- Side effects are visible in function signatures
- No hidden global state or implicit context
- Clear data flow
### State Machine Model
A state machine in Mindset consists of:
- **States**: Enum variants representing discrete system states
- **Transitions**: Allowed moves between states
- **Guards**: Pure functions determining if transition is allowed
- **Actions**: Effectful functions executed during transition
- **Environment**: External dependencies and services
### Effect-Based Transition Model
Transitions come in two flavors:
#### Pure Transitions (Zero Cost)
```rust
state.is_valid()
});
```
Compiles to direct state updates with no runtime overhead.
#### Effectful Transitions
```rust
if !state.is_valid() {
return Err("Invalid state");
}
// Effects
env.log("Transitioning");
env.save_to_db(state)?;
// State update
state.version += 1;
Ok(())
});
```
Effects are explicit via environment parameter. Only pay for what you use.
## Examples
Run any example with `cargo run --example <name>`:
| [basic_state_machine](examples/basic_state_machine.rs) | Zero-cost state machine with pure transitions |
| [effectful_state_machine](examples/effectful_state_machine.rs) | Environment pattern and effectful actions |
| [testing_patterns](examples/testing_patterns.rs) | Testing with mock environments |
| [traffic_light](examples/traffic_light.rs) | Simple cyclic state machine |
| [document_workflow](examples/document_workflow.rs) | Multi-stage approval workflow |
| [order_processing](examples/order_processing.rs) | E-commerce order lifecycle |
| [account_management](examples/account_management.rs) | Account states with validation |
| [checkpoint_resume](examples/checkpoint_resume.rs) | Checkpoint and resume patterns |
| [mapreduce_workflow](examples/mapreduce_workflow.rs) | MapReduce workflow implementation |
| [resource_management](examples/resource_management.rs) | Resource lifecycle management |
See [examples/](examples/) directory for full code and [examples/README.md](examples/README.md) for detailed explanations.
## Usage Examples
### Example 1: Traffic Light
```rust
use mindset::state_enum;
use mindset::builder::{StateMachineBuilder, simple_transition};
state_enum! {
enum TrafficLight {
Red,
Yellow,
Green,
}
}
let machine = StateMachineBuilder::new()
.initial(TrafficLight::Red)
.transitions(vec![
simple_transition(TrafficLight::Red, TrafficLight::Green),
simple_transition(TrafficLight::Green, TrafficLight::Yellow),
simple_transition(TrafficLight::Yellow, TrafficLight::Red),
])
.build()
.unwrap();
```
### Example 2: Document Workflow with Logging
```rust
use mindset::state_enum;
use mindset::builder::{StateMachineBuilder, TransitionBuilder};
state_enum! {
enum DocState {
Draft,
Review,
Approved,
Published,
}
final: [Published]
}
trait AuditLog {
fn log_transition(&mut self, from: DocState, to: DocState);
}
let machine = StateMachineBuilder::new()
.initial(DocState::Draft)
.add_transition(
TransitionBuilder::new()
.from(DocState::Draft)
.to(DocState::Review)
.succeeds()
.build()
.unwrap()
)
.add_transition(
TransitionBuilder::new()
.from(DocState::Review)
.to(DocState::Approved)
.succeeds()
.build()
.unwrap()
)
.build()
.unwrap();
```
### Example 3: State Machine with Validation
```rust
struct Account {
balance: f64,
status: AccountStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum AccountStatus {
Active,
Suspended,
Closed,
}
impl State for AccountStatus {}
trait AccountRepository {
fn persist(&mut self, account: &Account) -> Result<(), String>;
}
fn can_close(account: &Account) -> bool {
account.balance == 0.0
}
fn close_account<Env>(account: &mut Account, env: &mut Env) -> Result<(), String>
where
Env: AccountRepository,
{
if !can_close(account) {
return Err("Cannot close account with non-zero balance".to_string());
}
account.status = AccountStatus::Closed;
env.persist(account)?;
Ok(())
}
```
## Testing
The environment pattern makes testing straightforward:
```rust
#[cfg(test)]
mod tests {
use super::*;
struct MockEnv {
logged: Vec<String>,
saved: Vec<String>,
}
impl Logger for MockEnv {
fn log(&mut self, msg: &str) {
self.logged.push(msg.to_string());
}
}
impl Database for MockEnv {
fn save(&mut self, data: &str) -> Result<(), String> {
self.saved.push(data.to_string());
Ok(())
}
}
#[test]
fn test_effectful_transition() {
let mut state = State::Initial;
let mut env = MockEnv {
logged: vec![],
saved: vec![],
};
transition(&mut state, &mut env).unwrap();
assert_eq!(env.logged.len(), 1);
assert_eq!(env.saved.len(), 1);
}
}
```
## Performance
### Zero-Cost Pure Transitions
Pure transitions have zero runtime overhead. This code:
```rust
machine.transition(State::A, State::B);
```
Compiles to the same assembly as:
```rust
state = State::B;
```
### Effect Cost Model
Effects only cost what you use:
- **No effects**: Zero overhead (direct state update)
- **Single trait**: Monomorphized static dispatch (zero-cost abstraction)
- **Multiple traits**: One vtable lookup per trait (if using trait objects)
- **Environment mutation**: Direct field access
### Benchmarks
On a typical modern CPU:
- Pure transition: ~0.5ns (equivalent to direct assignment)
- Single-effect transition: ~2-3ns (includes function call overhead)
- Multi-effect transition: ~5-10ns (depends on effect complexity)
## Checkpoint and Resume
Mindset includes built-in checkpoint and resume functionality for long-running MapReduce workflows. This allows workflows to be paused and resumed without losing progress, making the system resilient to interruptions.
### Key Features
- **Automatic Checkpointing**: Workflows automatically save progress at key phases (map completion, reduce rounds)
- **Serialization Formats**: Support for both JSON (human-readable) and binary (compact) formats
- **Atomic Writes**: Checkpoint writes use atomic file operations to prevent corruption
- **Resume from Interruption**: Seamlessly continue workflows after crashes, stops, or planned maintenance
### Use Cases
Long-running workflows benefit from checkpointing when:
- Processing large datasets that take hours or days
- Running on infrastructure that may experience interruptions
- Needing to pause workflows during high-demand periods
- Debugging or inspecting intermediate workflow state
- Optimizing costs by pausing during expensive compute periods
### Quick Example
```bash
# Start a workflow
./workflow run --config workflow.yaml
# Workflow saves checkpoints automatically...
# Interrupt with Ctrl+C or system failure
# Resume from checkpoint
./workflow resume --checkpoint ./checkpoints/latest.json
# Workflow continues from where it left off
```
### Learn More
For detailed documentation on checkpoint structure, resume behavior, best practices, and atomic write patterns, see the [Checkpointing Guide](docs/checkpointing.md).
## Documentation
- [Builder Guide](docs/builder-guide.md): Comprehensive guide to the builder API with examples and patterns
- [Checkpointing Guide](docs/checkpointing.md): Checkpoint and resume for long-running workflows
- [Effects Guide](docs/effects-guide.md): Comprehensive guide to effect patterns
- [API Documentation](https://docs.rs/mindset): Generated API docs
## Design Philosophy
### Functional Core, Imperative Shell
- **Pure core**: Business logic and guards are pure functions
- **Effectful shell**: I/O and side effects at the boundaries
- Clear separation enables testing and reasoning
### Pay Only for What You Use
- Zero-cost when effects aren't needed
- Explicit opt-in for effects
- No hidden overhead or runtime costs
### Explicit Over Implicit
- Side effects visible in function signatures
- Environment dependencies declared as trait bounds
- No magic or hidden behavior
### Testability First
- Pure functions are trivial to test
- Mock environments for integration testing
- Clear dependency injection
## Project Status
This library implements the effect-based state machine foundation as specified in:
- **Spec 001**: Core state machine with pure guards
- **Spec 002**: Effect-based transitions with environment pattern
- **Spec 004**: Checkpoint and resume for persistence
- **Spec 005**: Builder API for ergonomic construction
## Contributing
Contributions are welcome! Please ensure:
- All tests pass
- Code follows project conventions
- Documentation is updated
- Commit messages are clear and descriptive
## License
[License information to be added]
## Further Reading
- [Stillwater 0.11.0 Documentation](https://docs.rs/stillwater)
- [Effects Guide](docs/effects-guide.md)
- [State Machine Patterns](docs/patterns.md)