mindset 0.1.1

A pure functional state machine library built on Stillwater's Effect system
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
# Effects Guide

This guide explains how to use Stillwater 0.11.0's effect system to build effectful state machines with zero-cost abstractions.

## Overview

Stillwater provides an effect-based transition model that separates pure guard logic from effectful actions. This enables:

- **Pure guards**: Deterministic state validation with no side effects
- **Effectful actions**: Explicit I/O and side effects in transitions
- **Zero-cost abstractions**: No runtime overhead when effects aren't needed
- **Dependency injection**: Clean environment passing for testability

## Core Concepts

### Pure Guards vs Effectful Actions

Guards determine whether a transition is allowed. They should be pure functions:

```rust
fn can_transition(state: &State) -> bool {
    state.is_valid() && state.count > 0
}
```

Actions perform side effects during transitions. They use the effect system:

```rust
fn perform_action<Env>(state: &mut State, env: &mut Env) -> Result<(), Error>
where
    Env: Logger + Database,
{
    env.log("Transitioning state");
    env.save_to_db(state)?;
    state.count += 1;
    Ok(())
}
```

### Zero-Cost Effect System

When you don't need effects, there's zero runtime overhead:

```rust
// No effects - compiles to zero-cost state transitions
let machine = StateMachine::builder()
    .state(Idle)
    .transition(Idle, Running, |_state| true)
    .build();
```

With effects, you pay only for what you use:

```rust
// Effectful transitions - explicit environment threading
let machine = StateMachine::builder()
    .state(Idle)
    .transition_with_effect(Idle, Running, |state, env| {
        env.log("Starting");
        Ok(())
    })
    .build();

// Execute with environment
machine.execute(&mut state, &mut env)?;
```

## Effect Patterns

### Environment Trait Pattern

Define your environment's capabilities as traits:

```rust
pub trait Logger {
    fn log(&mut self, message: &str);
}

pub trait Database {
    fn save(&mut self, data: &str) -> Result<(), Error>;
}

pub trait FileSystem {
    fn read_file(&self, path: &str) -> Result<String, Error>;
    fn write_file(&mut self, path: &str, content: &str) -> Result<(), Error>;
}
```

Compose environments from multiple traits:

```rust
struct AppEnv {
    logger: ConsoleLogger,
    db: SqliteDb,
}

impl Logger for AppEnv {
    fn log(&mut self, message: &str) {
        self.logger.log(message)
    }
}

impl Database for AppEnv {
    fn save(&mut self, data: &str) -> Result<(), Error> {
        self.db.save(data)
    }
}
```

### Effectful Transition Example

Here's a complete example of an effectful state machine:

```rust
use stillwater::{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,
}

trait PaymentGateway {
    fn charge(&mut self, amount: f64) -> Result<(), String>;
}

trait NotificationService {
    fn notify(&mut self, message: &str) -> Result<(), String>;
}

fn submit_order<Env>(order: &mut Order, env: &mut Env) -> Result<(), String>
where
    Env: PaymentGateway + NotificationService,
{
    env.charge(order.total)?;
    env.notify(&format!("Order {} submitted", order.id))?;
    Ok(())
}

fn main() {
    let machine = StateMachine::builder()
        .state(OrderState::Draft)
        .state(OrderState::Submitted)
        .transition_with_effect(
            OrderState::Draft,
            OrderState::Submitted,
            submit_order,
        )
        .build();

    // Use with real environment
    let mut order = Order { id: 123, total: 99.99 };
    let mut env = ProductionEnv::new();
    machine.transition(&mut order, &mut env).unwrap();
}
```

### Testing with Mock Environments

The environment pattern makes testing easy:

```rust
#[cfg(test)]
mod tests {
    use super::*;

    struct MockEnv {
        charges: Vec<f64>,
        notifications: Vec<String>,
    }

    impl PaymentGateway for MockEnv {
        fn charge(&mut self, amount: f64) -> Result<(), String> {
            self.charges.push(amount);
            Ok(())
        }
    }

    impl NotificationService for MockEnv {
        fn notify(&mut self, message: &str) -> Result<(), String> {
            self.notifications.push(message.to_string());
            Ok(())
        }
    }

    #[test]
    fn test_order_submission() {
        let mut order = Order { id: 123, total: 99.99 };
        let mut env = MockEnv {
            charges: vec![],
            notifications: vec![],
        };

        submit_order(&mut order, &mut env).unwrap();

        assert_eq!(env.charges.len(), 1);
        assert_eq!(env.charges[0], 99.99);
        assert_eq!(env.notifications.len(), 1);
    }
}
```

## Advanced Patterns

### Conditional Effects

Use guards to decide when effects should run:

```rust
fn maybe_send_notification<Env>(state: &State, env: &mut Env) -> Result<(), Error>
where
    Env: NotificationService,
{
    if state.should_notify {
        env.notify("State changed")?;
    }
    Ok(())
}
```

### Effect Composition

Compose multiple effects in a transition:

```rust
fn complex_transition<Env>(state: &mut State, env: &mut Env) -> Result<(), Error>
where
    Env: Logger + Database + NotificationService,
{
    env.log("Starting transition");

    // Validate
    if !state.is_valid() {
        return Err(Error::InvalidState);
    }

    // Persist
    env.save_to_db(&state.serialize()?)?;

    // Notify
    env.notify("State updated")?;

    // Update state
    state.version += 1;

    Ok(())
}
```

### Error Handling in Effects

Effects return `Result` for proper error handling:

```rust
fn safe_transition<Env>(state: &mut State, env: &mut Env) -> Result<(), Error>
where
    Env: Database,
{
    match env.save_to_db(state) {
        Ok(_) => {
            state.persisted = true;
            Ok(())
        }
        Err(e) => {
            env.log_error(&format!("Failed to save: {}", e));
            Err(Error::PersistenceFailed(e))
        }
    }
}
```

## Best Practices

### Keep Guards Pure

Guards should never have side effects:

```rust
// Good - pure guard
fn can_submit(order: &Order) -> bool {
    order.total > 0.0 && !order.items.is_empty()
}

// Bad - effectful guard
fn can_submit<Env>(order: &Order, env: &mut Env) -> bool {
    env.log("Checking if can submit"); // Side effect!
    order.total > 0.0
}
```

### Use Trait Bounds for Effects

Be explicit about what effects a transition needs:

```rust
// Good - explicit trait bounds
fn process<Env>(state: &mut State, env: &mut Env) -> Result<(), Error>
where
    Env: Database + Logger,
{
    env.log("Processing");
    env.save(state)?;
    Ok(())
}

// Avoid - generic environment without bounds
fn process<Env>(state: &mut State, env: &mut Env) -> Result<(), Error> {
    // What can env do? Unclear!
    Ok(())
}
```

### Separate Business Logic from I/O

Keep state transformations pure, effects at the boundaries:

```rust
// Pure business logic
fn calculate_total(items: &[Item]) -> f64 {
    items.iter().map(|i| i.price).sum()
}

// Effectful boundary
fn save_order<Env>(order: &mut Order, env: &mut Env) -> Result<(), Error>
where
    Env: Database,
{
    order.total = calculate_total(&order.items);
    env.save(order)?;
    Ok(())
}
```

### Use Zero-Cost When Possible

If you don't need effects, don't use them:

```rust
// Simple state machine - no effects needed
let machine = StateMachine::builder()
    .state(State::A)
    .state(State::B)
    .transition(State::A, State::B, |_| true)
    .build();
```

Only add effects when necessary:

```rust
// Now we need logging - add it explicitly
let machine = StateMachine::builder()
    .state(State::A)
    .state(State::B)
    .transition_with_effect(State::A, State::B, |_, env: &mut Logger| {
        env.log("Transitioning");
        Ok(())
    })
    .build();
```

## Performance Characteristics

### Zero-Cost Pure Transitions

Pure transitions compile to direct function calls with no overhead:

```rust
// This has zero runtime cost over manual state updates
machine.transition(State::A, State::B);
```

### Effect Cost Model

Effects only cost what you use:

- **No effects**: Zero overhead
- **Single trait**: One vtable lookup (if dynamic) or monomorphized (if static)
- **Multiple traits**: One lookup per trait
- **Environment mutation**: Direct field access, no allocation

### Optimization Tips

1. **Use static dispatch when possible**: Generic `Env` parameter allows monomorphization
2. **Keep environment types small**: Pass references to services, not large structs
3. **Batch effects**: Combine multiple state changes in one transition
4. **Avoid allocations**: Use references and borrows in effect signatures

## Migration Guide

### From Effectful Guards

If you have guards with side effects:

```rust
// Before
fn guard_with_logging<Env>(state: &State, env: &mut Env) -> bool {
    env.log("Checking guard");
    state.is_valid()
}
```

Split into pure guard and effectful action:

```rust
// After
fn guard(state: &State) -> bool {
    state.is_valid()
}

fn action<Env>(state: &mut State, env: &mut Env) -> Result<(), Error> {
    env.log("Transitioning");
    Ok(())
}
```

### From Monolithic Actions

If you have large effectful functions:

```rust
// Before
fn big_transition<Env>(state: &mut State, env: &mut Env) -> Result<(), Error> {
    env.log("Starting");
    let data = calculate_data(state);
    env.save(data)?;
    state.counter += 1;
    env.notify("Done");
    Ok(())
}
```

Extract pure logic:

```rust
// After
fn calculate_data(state: &State) -> Data {
    // Pure calculation
    Data::from(state)
}

fn transition<Env>(state: &mut State, env: &mut Env) -> Result<(), Error>
where
    Env: Logger + Database + NotificationService,
{
    env.log("Starting");

    let data = calculate_data(state);
    env.save(data)?;

    state.counter += 1;

    env.notify("Done");
    Ok(())
}
```

## Summary

Stillwater's effect system provides:

- **Pure guards**: Deterministic, testable state validation
- **Explicit effects**: Side effects only where needed
- **Zero-cost abstraction**: No overhead when effects aren't used
- **Dependency injection**: Clean environment pattern for testing
- **Composable traits**: Build complex environments from simple pieces

Use effects to keep your state machines testable, maintainable, and performant.