Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. Validation: Accumulating ALL errors instead of failing on the first one
  2. 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

  1. Semigroup - The foundation for combining errors

    • What is a Semigroup?
    • Why it matters for validation
    • Implementing Semigroup for your types
  2. Validation - Error accumulation

    • The problem with Result
    • Using Validation for error accumulation
    • Combining validations
    • Real-world examples
  3. Effects - Pure core, imperative shell

    • Separating logic from I/O
    • Effect composition
    • Testing effectful code
    • Async support
  4. Error Context - Better debugging

    • Adding context to errors
    • Error trails
    • Best practices
  5. IO Module - Ergonomic helpers

    • IO::read, IO::write, IO::read_async, IO::write_async
    • Dependency injection patterns
    • Testing with mock environments
  6. Helper Combinators - Common patterns

    • traverse, sequence
    • Convenience functions
    • Building your own combinators
  7. Try Trait - Nightly feature

    • Using ? with Validation
    • When to enable try_trait
    • Migration path
  8. 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

  1. Reader Pattern - Dependency injection through environments

    • Asking for environment values
    • Dependency injection without singletons
    • Testing with mock environments
  2. Parallel Effects - Running independent effects concurrently

  • Heterogeneous parallel effects
  • Homogeneous parallel collections
  • Error behavior and environment sharing
  1. Traverse Patterns - Working with collections
  • Collection validation with error accumulation
  • Effect processing over collections
  • Batch operations
  • Traverse vs sequence
  • Real-world examples
  1. 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

  1. Homogeneous Validation - Type-consistent error accumulation
  • Combining enum variants safely
  • Preventing incompatible accumulation
  • Validation patterns for homogeneous data
  1. 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

  1. 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

  1. 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 CaseToolExample
Form validationValidationCollect all field errors
API request validationValidationReturn all validation errors
Collection validationtraverseValidate multiple items, accumulate errors
Batch processingtraverse_effectProcess collection with effects
Database operationsEffectSeparate logic from I/O
File operationsEffect + IOTestable file processing
Error debuggingContextErrorAdd context trails
Error recoveryrecover() / recover_with()Fallback on specific errors
Cache fallbackrecover()Try cache, fallback to DB
Graceful degradationrecover_some()Provide reduced functionality
Default valuesfallback()Use default on any error
Data aggregationMonoidCombine collections with fold_all
Numeric operationsSum/ProductAggregate numbers with identity
Retry transient errorsretryRetry with backoff strategies
Conditional retryretry_ifRetry only on certain errors
Timeout operationswith_timeoutPrevent hanging operations
Testing validationsassert_success! / assert_failure!Concise test assertions
Testing effectsTestEffectDeterministic effect testing
Mock environmentsMockEnvBuild test dependencies
Property-based testsproptest featureGenerate test cases
Resource acquisition.acquires::()Mark effect as acquiring resource
Resource release.releases::()Mark effect as releasing resource
Safe resource opsresource_bracketGuaranteed acquire/use/release
Resource neutralityassert_resource_neutralCompile-time leak detection
Type-level invariantsRefined<T, P>Guarantee constraints at compile time
Parse, don’t validateNonEmptyString, PortValidate 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

Next Steps

Ready to dive in? Start with Chapter 1: Semigroup!