Stillwater User Guide
Welcome to the Stillwater user guide! This guide will help you understand and master the core concepts of Stillwater.
What is Stillwater?
Stillwater is a Rust library for pragmatic functional programming focused on two main problems:
- Validation: Accumulating ALL errors instead of failing on the first one
- Effects: Separating pure business logic from I/O operations
Guide Structure
This guide is organized into progressive chapters, each building on the previous:
Core Concepts
-
Semigroup - The foundation for combining errors
- What is a Semigroup?
- Why it matters for validation
- Implementing Semigroup for your types
-
Validation - Error accumulation
- The problem with Result
- Using Validation for error accumulation
- Combining validations
- Real-world examples
-
Effects - Pure core, imperative shell
- Separating logic from I/O
- Effect composition
- Testing effectful code
- Async support
-
Error Context - Better debugging
- Adding context to errors
- Error trails
- Best practices
-
IO Module - Ergonomic helpers
- IO::read, IO::write, IO::read_async, IO::write_async
- Dependency injection patterns
- Testing with mock environments
-
Helper Combinators - Common patterns
- traverse, sequence
- Convenience functions
- Building your own combinators
-
Try Trait - Nightly feature
- Using ? with Validation
- When to enable try_trait
- Migration path
-
Monoid - Identity elements for composition
- What is a Monoid?
- Extending Semigroup with identity
- Numeric monoids (Sum, Product, Max, Min)
- Using fold_all and reduce
- Real-world aggregation patterns
Effect Patterns
-
Reader Pattern - Dependency injection through environments
- Asking for environment values
- Dependency injection without singletons
- Testing with mock environments
-
Parallel Effects - Running independent effects concurrently
- Heterogeneous parallel effects
- Homogeneous parallel collections
- Error behavior and environment sharing
- Traverse Patterns - Working with collections
- Collection validation with error accumulation
- Effect processing over collections
- Batch operations
- Traverse vs sequence
- Real-world examples
- Retry and Resilience - Handling transient failures
- Retry policies and backoff strategies
- Conditional retry with retry_if
- Observability hooks for logging/metrics
- Timeout support
- Combining retry with timeout
Validation Patterns
- Homogeneous Validation - Type-consistent error accumulation
- Combining enum variants safely
- Preventing incompatible accumulation
- Validation patterns for homogeneous data
- Refined Types - Parse, don’t validate
Refined<T, P>for type-level invariants- Numeric predicates: Positive, NonNegative, NonZero, InRange
- String predicates: NonEmpty, Trimmed, MaxLength, MinLength
- Predicate combinators: And, Or, Not
- Validation integration for error accumulation
- Zero-cost: same memory layout as inner type
Testing & Quality
- Testing - Testing utilities and patterns
- MockEnv builder for test environments
- Assertion macros for Validation
- TestEffect for deterministic testing
- Property-based testing with proptest
- Testing best practices
Additional Advanced Topics
- Compile-Time Resource Tracking - Type-level resource safety
- Resource markers (FileRes, DbRes, TxRes, etc.)
- ResourceEffect trait with Acquires/Releases tracking
- Extension methods:
.acquires(),.releases(),.neutral() - resource_bracket for guaranteed cleanup
- Zero runtime overhead - purely type-level
How to Use This Guide
For Beginners
Start with chapters 1-3 in order. These cover the core concepts you need to be productive with Stillwater.
For Experienced Users
Jump to the chapters that interest you. Each chapter is self-contained with links to related concepts.
Running Examples
All examples in this guide are runnable. You can find them in the examples/ directory:
cargo run --example validation
cargo run --example effects
cargo run --example monoid
cargo run --example form_validation
cargo run --example recover_patterns
cargo run --example retry_patterns
cargo run --example resource_tracking
Quick Reference
When to Use What
| Use Case | Tool | Example |
|---|---|---|
| Form validation | Validation | Collect all field errors |
| API request validation | Validation | Return all validation errors |
| Collection validation | traverse | Validate multiple items, accumulate errors |
| Batch processing | traverse_effect | Process collection with effects |
| Database operations | Effect | Separate logic from I/O |
| File operations | Effect + IO | Testable file processing |
| Error debugging | ContextError | Add context trails |
| Error recovery | recover() / recover_with() | Fallback on specific errors |
| Cache fallback | recover() | Try cache, fallback to DB |
| Graceful degradation | recover_some() | Provide reduced functionality |
| Default values | fallback() | Use default on any error |
| Data aggregation | Monoid | Combine collections with fold_all |
| Numeric operations | Sum/Product | Aggregate numbers with identity |
| Retry transient errors | retry | Retry with backoff strategies |
| Conditional retry | retry_if | Retry only on certain errors |
| Timeout operations | with_timeout | Prevent hanging operations |
| Testing validations | assert_success! / assert_failure! | Concise test assertions |
| Testing effects | TestEffect | Deterministic effect testing |
| Mock environments | MockEnv | Build test dependencies |
| Property-based tests | proptest feature | Generate test cases |
| Resource acquisition | .acquires:: | Mark effect as acquiring resource |
| Resource release | .releases:: | Mark effect as releasing resource |
| Safe resource ops | resource_bracket | Guaranteed acquire/use/release |
| Resource neutrality | assert_resource_neutral | Compile-time leak detection |
| Type-level invariants | Refined<T, P> | Guarantee constraints at compile time |
| Parse, don’t validate | NonEmptyString, Port | Validate once, use safely everywhere |
Common Patterns
#![allow(unused)]
fn main() {
use stillwater::prelude::*;
// Pattern 1: Independent validations
Validation::all((
validate_email(input.email),
validate_age(input.age),
))
// Pattern 2: Dependent validations
validate_email(email)
.and_then(|email| check_email_available(email))
// Pattern 3: Effect with validation
Effect::from_validation(validate_user(input))
.and_then(|user| save_to_db(user))
// Pattern 4: Error recovery with fallback
fetch_from_cache(key)
.recover(
|e| matches!(e, CacheError::Miss),
|_| fetch_from_db(key)
)
}
Getting Help
- Check the FAQ for common questions
- Read the API documentation
- See PATTERNS.md for recipes
- Compare to other libraries
Next Steps
Ready to dive in? Start with Chapter 1: Semigroup!