id_effect 0.2.0

Effect<A, E, R> (sync + async), context/layers, pipe — interpreter-style, no bundled executor
Documentation
# Your First Real Program

Let's build something complete: a small program that loads configuration, connects to a database, queries a user, and formats a greeting. It's simple enough to fit on one page, but real enough to demonstrate the full effect workflow.

## The Domain

```rust
#[derive(Debug)]
struct Config {
    db_url: String,
    app_name: String,
}

#[derive(Debug)]
struct User {
    id: u64,
    name: String,
    email: String,
}

#[derive(Debug)]
enum AppError {
    Config(String),
    Database(String),
}
```

## The Individual Steps

Each step is a focused effect:

```rust
use id_effect::{Effect, effect, succeed, fail};

fn load_config() -> Effect<Config, AppError, ()> {
    // In a real app, read from a file or env vars
    succeed(Config {
        db_url: "postgres://localhost/myapp".to_string(),
        app_name: "Greeter".to_string(),
    })
}

fn connect_db(config: &Config) -> Effect<Database, AppError, ()> {
    Database::connect(&config.db_url)
        .map_error(|e| AppError::Database(format!("connect: {e}")))
}

fn fetch_user(db: &Database, id: u64) -> Effect<User, AppError, ()> {
    db.query_user(id)
        .map_error(|e| AppError::Database(format!("query: {e}")))
}

fn format_greeting(config: &Config, user: &User) -> String {
    format!("{}: Hello, {}! ({})", config.app_name, user.name, user.email)
}
```

## Composing the Program

Now we compose these steps into one effect using `effect!`:

```rust
fn greet_user(user_id: u64) -> Effect<String, AppError, ()> {
    effect! {
        let config = ~ load_config();
        let db     = ~ connect_db(&config);
        let user   = ~ fetch_user(&db, user_id);
        format_greeting(&config, &user)
    }
}
```

Read it like a recipe:
1. Load config — if it fails, stop with `AppError::Config`
2. Connect to DB — if it fails, stop with `AppError::Database`  
3. Fetch user — if it fails, stop with `AppError::Database`
4. Format the greeting — this is pure, always succeeds

Nothing has run yet. `greet_user(42)` is a value.

## Running It

At the edge of the program — in `main` — we execute:

```rust
fn main() {
    match run_blocking(greet_user(42)) {
        Ok(greeting) => println!("{greeting}"),
        Err(AppError::Config(msg)) => eprintln!("Config error: {msg}"),
        Err(AppError::Database(msg)) => eprintln!("DB error: {msg}"),
    }
}
```

## Testing It

Because the effect is a description, testing is straightforward — just swap out the underlying steps:

```rust
#[test]
fn test_greeting_format() {
    let effect = effect! {
        let config = ~ succeed(Config {
            db_url: "unused".into(),
            app_name: "TestApp".into(),
        });
        let user = ~ succeed(User {
            id: 1,
            name: "Alice".into(),
            email: "alice@example.com".into(),
        });
        format_greeting(&config, &user)
    };

    let result = run_test(effect);
    assert_eq!(result.unwrap(), "TestApp: Hello, Alice! (alice@example.com)");
}
```

No mocking framework. No `Arc<dyn Trait>` plumbing. Just substitute different `succeed` values for the steps you want to control.

## What You Just Learned

You've written a complete effect-based program. Along the way you used:

- `succeed` and `fail` to construct effects from values
- `.map` and `.map_error` to transform success and error types
- `effect! { ~ ... }` to sequence effects without callback nesting
- `run_blocking` to execute at the program edge
- `run_test` to verify behaviour in tests

That's the core of 90% of what you'll write day-to-day. The next two chapters go deeper: Chapter 3 explores the `effect!` macro in detail, and Chapter 4 begins the tour of `R` — the environment type that makes dependency injection a compile-time guarantee.

You just wrote your first effect-based program. It won't be your last.