{"id":30898100,"url":"https://github.com/dashed/algae","last_synced_at":"2026-08-05T03:31:35.553Z","repository":{"id":299317999,"uuid":"1002238991","full_name":"dashed/algae","owner":"dashed","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-15T22:53:52.000Z","size":304,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-15T23:58:06.269Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dashed.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-15T02:55:05.000Z","updated_at":"2025-06-15T22:53:55.000Z","dependencies_parsed_at":"2025-06-16T00:08:50.857Z","dependency_job_id":null,"html_url":"https://github.com/dashed/algae","commit_stats":null,"previous_names":["dashed/algae"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dashed/algae","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dashed%2Falgae","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dashed%2Falgae/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dashed%2Falgae/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dashed%2Falgae/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dashed","download_url":"https://codeload.github.com/dashed/algae/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dashed%2Falgae/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274231770,"owners_count":25245855,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-09T01:05:34.183Z","updated_at":"2025-10-08T02:54:49.729Z","avatar_url":"https://github.com/dashed.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Algae - Algebraic Effects for Rust 🦀\n\n\u003c!-- [![Crates.io](https://img.shields.io/crates/v/algae.svg)](https://crates.io/crates/algae) --\u003e\n\u003c!-- [![Documentation](https://docs.rs/algae/badge.svg)](https://docs.rs/algae) --\u003e\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n---\n\n\u003e # ⚠️ **EXPERIMENTAL TOY PROJECT** ⚠️\n\u003e \n\u003e **This is a research/educational project and is NOT ready for production use.**\n\u003e \n\u003e - 🚧 **Unstable API**: Everything may change without notice\n\u003e - 📚 **Learning Purpose**: Built primarily for exploring algebraic effects in Rust\n\u003e - ⚠️ **Use at Your Own Risk**: Not suitable for any production systems\n\u003e - 🧪 **Experimental**: Relies on unstable Rust nightly features\n\n---\n\n**Algae** is a Rust library that brings the power of algebraic effects to systems programming. It provides a clean, type-safe way to handle side effects in your programs while maintaining composability, testability, and performance.\n\nAlgae implements **one-shot (linear) algebraic effects**, where each effect operation receives exactly one response and continuations are not captured for reuse. This design choice prioritizes simplicity, performance, and ease of understanding while covering the vast majority of real-world use cases.\n\n## 🎯 What are Algebraic Effects?\n\nAlgebraic effects are a programming paradigm that allows you to separate the **description** of side effects from their **implementation**. Think of them as a more powerful and composable alternative to traditional approaches like dependency injection or the strategy pattern.\n\n### Key Benefits\n\n- **🔄 Composable**: Effects can be combined and nested naturally\n- **🧪 Testable**: Easy to mock and test effectful code\n- **🎭 Polymorphic**: Same code can run with different implementations\n- **🔒 Type-safe**: All effects are statically checked at compile time\n- **⚡ Low-cost**: Minimal runtime overhead using efficient Rust coroutines\n- **📏 Linear**: One-shot effects ensure predictable, easy-to-reason-about control flow\n\n## 🚀 Quick Start\n\nAdd algae to your `Cargo.toml`:\n\n\u003e **⚠️ Note**: Algae is not yet published to Crates.io. For now, you'll need to use it as a Git dependency:\n\n```toml\n[dependencies]\nalgae = { git = \"https://github.com/your-username/algae.git\" }\n```\n\nOr clone the repository and use it as a local dependency:\n\n```toml\n[dependencies]\nalgae = { path = \"../algae\" }\n```\n\nEnable the required nightly features in your `src/main.rs` or `lib.rs`:\n\n```rust\n#![feature(coroutines, coroutine_trait, yield_expr)]\n```\n\nHere's a step-by-step example showing both the explicit and convenient approaches:\n\n```rust\n#![feature(coroutines, coroutine_trait, yield_expr)]\nuse algae::prelude::*;\n\n// 1. Define your effects\neffect! {\n    Console::Print (String) -\u003e ();\n    Console::ReadLine -\u003e String;\n}\n\n// 2a. Write effectful functions (explicit approach)\nfn greet_user_explicit() -\u003e Effectful\u003cString, Op\u003e {\n    Effectful::new(#[coroutine] move |mut _reply: Option\u003cReply\u003e| {\n        // Print prompt\n        {\n            let effect = Effect::new(Console::Print(\"What's your name?\".to_string()).into());\n            let reply_opt = yield effect;\n            let _: () = reply_opt.unwrap().take::\u003c()\u003e();\n        }\n        \n        // Read input\n        let name: String = {\n            let effect = Effect::new(Console::ReadLine.into());\n            let reply_opt = yield effect;\n            reply_opt.unwrap().take::\u003cString\u003e()\n        };\n        \n        format!(\"Hello, {}!\", name)\n    })\n}\n\n// 2b. Write effectful functions (convenient approach - same behavior!)\n#[effectful]\nfn greet_user() -\u003e String {\n    let _: () = perform!(Console::Print(\"What's your name?\".to_string()));\n    let name: String = perform!(Console::ReadLine);\n    format!(\"Hello, {}!\", name)\n}\n\n// 3. Implement handlers (same for both approaches)\nstruct RealConsoleHandler;\n\nimpl Handler\u003cOp\u003e for RealConsoleHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            Op::Console(Console::Print(msg)) =\u003e {\n                println!(\"{}\", msg);\n                Box::new(())\n            }\n            Op::Console(Console::ReadLine) =\u003e {\n                let mut input = String::new();\n                std::io::stdin().read_line(\u0026mut input).unwrap();\n                Box::new(input.trim().to_string())\n            }\n        }\n    }\n}\n\n// 4. Run with handlers (both functions work identically)\nfn main() {\n    // Both approaches produce the same result\n    let result1 = greet_user_explicit()\n        .handle(RealConsoleHandler)\n        .run();\n    \n    let result2 = greet_user()\n        .handle(RealConsoleHandler)\n        .run();\n    \n    println!(\"Explicit result: {}\", result1);\n    println!(\"Convenient result: {}\", result2);\n    // Both print the same thing!\n}\n```\n\n**Key insight:** The `#[effectful]` macro is pure convenience - it generates exactly the same `Effectful\u003cR, Op\u003e` type and runtime behavior as the explicit approach, but with much cleaner syntax.\n\n\u003e **📁 Working Examples**: \n\u003e - [`examples/readme.rs`](algae/examples/readme.rs) - Complete version of this code with real and mock handlers\n\u003e - [`examples/explicit_vs_convenient.rs`](algae/examples/explicit_vs_convenient.rs) - Side-by-side comparison proving both approaches are identical\n\u003e - [`examples/test_send_across_threads.rs`](algae/examples/test_send_across_threads.rs) - Demonstrates thread-safe effectful computations\n\n## 📚 Core Concepts\n\nUnderstanding algae requires familiarity with several key types and concepts. This section provides a comprehensive guide to all the core library components and how they work together.\n\n### 🎭 Effects as Descriptions\n\nEffects in algae are **descriptions** of what you want to do, not how to do it. They're defined using the `effect!` macro, which generates Rust enums representing your operations:\n\n```rust\neffect! {\n    // Each line defines an operation: Family::Operation (Parameters) -\u003e ReturnType\n    FileSystem::Read (String) -\u003e Result\u003cString, std::io::Error\u003e;\n    FileSystem::Write ((String, String)) -\u003e Result\u003c(), std::io::Error\u003e;\n    \n    Database::Query (String) -\u003e Vec\u003cRow\u003e;\n    Database::Execute (String) -\u003e Result\u003cu64, DbError\u003e;\n    \n    Logger::Info (String) -\u003e ();\n    Logger::Error (String) -\u003e ();\n}\n```\n\nThe `effect!` macro generates several types for you:\n\n```rust\n// Generated effect family enums\npub enum FileSystem {\n    Read(String),\n    Write((String, String)),\n}\n\npub enum Database {\n    Query(String),\n    Execute(String),\n}\n\npub enum Logger {\n    Info(String),\n    Error(String),\n}\n\n// Generated unified operation type\npub enum Op {\n    FileSystem(FileSystem),\n    Database(Database),\n    Logger(Logger),\n}\n\n// Generated conversion traits\nimpl From\u003cFileSystem\u003e for Op { ... }\nimpl From\u003cDatabase\u003e for Op { ... }\nimpl From\u003cLogger\u003e for Op { ... }\n```\n\n### 🔧 `Effectful\u003cR, Op\u003e` - Effectful Computations\n\nThe `Effectful\u003cR, Op\u003e` struct is the heart of algae. It represents a computation that:\n- **May perform effects** of type `Op` during execution\n- **Eventually produces a result** of type `R`\n- **Can be run with different handlers** for different behaviors\n\n```rust\n// Type signature breakdown:\n// Effectful\u003cR, Op\u003e\n//          │  └── The type of effects this computation can perform\n//          └────── The type of result this computation produces\n\n// Example: A computation that performs Console and Math effects and returns an i32\ntype MyComputation = Effectful\u003ci32, Op\u003e;\n```\n\n#### Creating Effectful Computations (The Explicit Way)\n\nLet's first see how to create effectful computations explicitly to understand what's happening under the hood:\n\n```rust\nuse algae::prelude::*;\n\n// Explicit function that returns Effectful\u003cR, Op\u003e\nfn calculate_with_logging_explicit(x: i32, y: i32) -\u003e Effectful\u003ci32, Op\u003e {\n    Effectful::new(#[coroutine] move |mut _reply: Option\u003cReply\u003e| {\n        // Manually perform Logger::Info effect\n        {\n            let effect = Effect::new(Logger::Info(format!(\"Calculating {} + {}\", x, y)).into());\n            let reply_opt = yield effect;\n            let _: () = reply_opt.unwrap().take::\u003c()\u003e();\n        }\n        \n        // Manually perform Math::Add effect\n        let result: i32 = {\n            let effect = Effect::new(Math::Add((x, y)).into());\n            let reply_opt = yield effect;\n            reply_opt.unwrap().take::\u003ci32\u003e()\n        };\n        \n        // Manually perform another Logger::Info effect\n        {\n            let effect = Effect::new(Logger::Info(format!(\"Result: {}\", result)).into());\n            let reply_opt = yield effect;\n            let _: () = reply_opt.unwrap().take::\u003c()\u003e();\n        }\n        \n        result\n    })\n}\n```\n\nThis explicit approach shows exactly what's happening:\n1. **Return type is explicit**: `Effectful\u003ci32, Op\u003e` - no magic\n2. **Coroutine creation**: We manually create the coroutine with `Effectful::new()`\n3. **Effect operations**: Each effect is manually created, yielded, and the reply extracted\n4. **Type safety**: We explicitly specify the expected return types\n\n#### Creating Effectful Computations (The Convenient Way)\n\nWriting coroutines manually is verbose and error-prone. The `#[effectful]` attribute and `perform!` macro automate this boilerplate:\n\n```rust\n#[effectful]\nfn calculate_with_logging(x: i32, y: i32) -\u003e i32 {\n    let _: () = perform!(Logger::Info(format!(\"Calculating {} + {}\", x, y)));\n    let result: i32 = perform!(Math::Add((x, y)));\n    let _: () = perform!(Logger::Info(format!(\"Result: {}\", result)));\n    result\n}\n// Actually returns: Effectful\u003ci32, Op\u003e (macro transforms the return type)\n```\n\n**What the `#[effectful]` macro does:**\n1. **Transforms return type**: `i32` → `Effectful\u003ci32, Op\u003e`\n2. **Wraps function body**: Creates the coroutine automatically\n3. **Enables `perform!`**: Lets you use the convenient effect syntax\n\n**What the `perform!` macro does:**\n1. **Creates the effect**: `Effect::new(operation.into())`\n2. **Yields to handler**: `yield effect`\n3. **Extracts the reply**: `reply.unwrap().take::\u003cExpectedType\u003e()`\n\n#### Why Use `#[effectful]`?\n\nThe explicit approach is educational but impractical for real code:\n\n| **Explicit Approach** | **`#[effectful]` Approach** |\n|----------------------|---------------------------|\n| ❌ **Verbose**: 7 lines per effect | ✅ **Concise**: 1 line per effect |\n| ❌ **Error-prone**: Manual type annotations | ✅ **Safe**: Automatic type inference |\n| ❌ **Repetitive**: Same pattern every time | ✅ **DRY**: Macro handles boilerplate |\n| ❌ **Hard to read**: Focus on mechanics | ✅ **Clear intent**: Focus on business logic |\n| ✅ **Educational**: Shows what's happening | ✅ **Productive**: Gets work done |\n\n**Equivalence guarantee:** Both approaches produce identical `Effectful\u003cR, Op\u003e` values and have the same runtime behavior.\n\n#### Running Effectful Computations\n\n`Effectful\u003cR, Op\u003e` provides methods for execution:\n\n```rust\nlet computation = calculate_with_logging(5, 3);\n\n// Method 1: Direct execution with handler\nlet result: i32 = computation.run_with(MyHandler::new());\n\n// Method 2: Fluent API (recommended)\nlet result: i32 = computation\n    .handle(MyHandler::new())  // Returns Handled\u003ci32, Op, MyHandler\u003e\n    .run();                    // Returns i32\n```\n\n### 🛠️ `Handler\u003cOp\u003e` - Effect Implementations\n\nThe `Handler\u003cOp\u003e` trait defines how effects are actually executed. Handlers are the \"interpreters\" that give meaning to your effect descriptions:\n\n```rust\npub trait Handler\u003cOp\u003e {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e;\n}\n```\n\n#### Type-Safe Effect Handling\n\nAlthough the return type is type-erased (`Box\u003cdyn Any + Send\u003e`), algae ensures type safety through the effect system:\n\n```rust\nstruct MyHandler {\n    log_count: usize,\n}\n\nimpl Handler\u003cOp\u003e for MyHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            // Each branch must return the type specified in the effect! declaration\n            Op::Logger(Logger::Info(msg)) =\u003e {\n                println!(\"INFO: {}\", msg);\n                self.log_count += 1;\n                Box::new(())  // Must return () as declared\n            }\n            Op::Math(Math::Add((a, b))) =\u003e {\n                Box::new(a + b)  // Must return i32 as declared\n            }\n            Op::FileSystem(FileSystem::Read(path)) =\u003e {\n                Box::new(std::fs::read_to_string(path))  // Must return Result\u003cString, std::io::Error\u003e\n            }\n        }\n    }\n}\n```\n\n#### Handler Patterns\n\n**Production Handler:**\n```rust\nstruct ProductionHandler {\n    db_pool: ConnectionPool,\n    logger: Logger,\n}\n\nimpl Handler\u003cOp\u003e for ProductionHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            Op::Database(Database::Query(sql)) =\u003e {\n                let rows = self.db_pool.execute(sql).unwrap();\n                Box::new(rows)\n            }\n            Op::Logger(Logger::Info(msg)) =\u003e {\n                self.logger.info(msg);\n                Box::new(())\n            }\n        }\n    }\n}\n```\n\n**Test Handler:**\n```rust\nstruct MockHandler {\n    db_responses: HashMap\u003cString, Vec\u003cRow\u003e\u003e,\n    logged_messages: Vec\u003cString\u003e,\n}\n\nimpl Handler\u003cOp\u003e for MockHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            Op::Database(Database::Query(sql)) =\u003e {\n                let rows = self.db_responses.get(sql).cloned().unwrap_or_default();\n                Box::new(rows)\n            }\n            Op::Logger(Logger::Info(msg)) =\u003e {\n                self.logged_messages.push(msg.clone());\n                Box::new(())\n            }\n        }\n    }\n}\n```\n\n### ⚡ `Effect\u003cOp\u003e` and `Reply` - The Runtime Types\n\nThese are the low-level types that power the effect system. You typically don't use them directly, but understanding them helps you understand how algae works internally.\n\n#### `Effect\u003cOp\u003e` - Effect Requests\n\nAn `Effect\u003cOp\u003e` represents a single effect operation that has been requested but not yet handled:\n\n```rust\npub struct Effect\u003cOp\u003e {\n    pub op: Op,                           // The operation being requested\n    reply: Option\u003cBox\u003cdyn Any + Send\u003e\u003e,   // Storage for the handler's response\n}\n```\n\n```rust\n// Created automatically by perform!() macro\nlet effect = Effect::new(Logger::Info(\"Hello\".to_string()));\n\n// Handler fills the effect with a response\neffect.fill_boxed(Box::new(()));\n\n// Extract the response\nlet reply = effect.get_reply();\n```\n\n#### `Reply` - Typed Response Extraction\n\nA `Reply` wraps the handler's response and provides type-safe extraction:\n\n```rust\npub struct Reply {\n    value: Box\u003cdyn Any + Send\u003e,  // Type-erased response from handler\n}\n\nimpl Reply {\n    pub fn take\u003cR: Any + Send\u003e(self) -\u003e R {\n        // Runtime type checking + extraction\n        // Panics if types don't match\n    }\n}\n```\n\n```rust\n// Created when extracting from Effect\nlet reply: Reply = effect.get_reply();\n\n// Type-safe extraction (must match effect declaration)\nlet response: () = reply.take::\u003c()\u003e();  // For Logger::Info -\u003e ()\nlet result: i32 = reply.take::\u003ci32\u003e();   // For Math::Add -\u003e i32\n```\n\n### 🔄 The Execution Model\n\nUnderstanding how algae executes effectful computations helps you write better code and debug issues:\n\n#### 1. Compilation Phase\n\n```rust\n// What you write with the convenient syntax:\n#[effectful]\nfn my_function() -\u003e String {\n    let value: i32 = perform!(Math::Add((2, 3)));\n    format!(\"Result: {}\", value)\n}\n\n// What the macros generate (equivalent to explicit approach):\nfn my_function() -\u003e Effectful\u003cString, Op\u003e {\n    Effectful::new(#[coroutine] move |mut _reply: Option\u003cReply\u003e| {\n        // perform!(Math::Add((2, 3))) expands to:\n        let value: i32 = {\n            let __eff = Effect::new(Math::Add((2, 3)).into());\n            let __reply_opt = yield __eff;\n            __reply_opt.unwrap().take::\u003ci32\u003e()\n        };\n        format!(\"Result: {}\", value)\n    })\n}\n\n// This is identical to what you'd write explicitly:\nfn my_function_explicit() -\u003e Effectful\u003cString, Op\u003e {\n    Effectful::new(#[coroutine] move |mut _reply: Option\u003cReply\u003e| {\n        let value: i32 = {\n            let effect = Effect::new(Math::Add((2, 3)).into());\n            let reply_opt = yield effect;\n            reply_opt.unwrap().take::\u003ci32\u003e()\n        };\n        format!(\"Result: {}\", value)\n    })\n}\n```\n\n#### 2. Execution Phase\n\n```rust\nlet computation = my_function();\nlet result = computation.handle(MyHandler::new()).run();\n```\n\n**Step-by-step execution:**\n\n1. **Start coroutine** with `None` (no previous reply)\n2. **Hit `perform!`** - creates `Effect::new(Math::Add((2, 3)))`\n3. **Yield effect** to handler and suspend coroutine\n4. **Handler processes** `Math::Add((2, 3))` and returns `Box::new(5i32)`\n5. **Fill effect** with handler's response\n6. **Resume coroutine** with `Some(Reply { value: Box::new(5i32) })`\n7. **Extract result** using `reply.take::\u003ci32\u003e()` → `5i32`\n8. **Continue execution** with the extracted value\n9. **Return final result** `\"Result: 5\"`\n\n#### 3. Type Safety at Runtime\n\n```rust\n// Effect declaration says Math::Add returns i32\neffect! {\n    Math::Add ((i32, i32)) -\u003e i32;\n}\n\n// Handler must return i32 (but as Box\u003cdyn Any + Send\u003e)\nimpl Handler\u003cOp\u003e for MyHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            Op::Math(Math::Add((a, b))) =\u003e Box::new(a + b), // ✅ Returns i32\n            // Op::Math(Math::Add((a, b))) =\u003e Box::new(\"hello\"), // ❌ Would panic at runtime\n        }\n    }\n}\n\n// perform! expects i32 (enforced at runtime)\nlet value: i32 = perform!(Math::Add((2, 3))); // ✅ Type matches\n// let value: String = perform!(Math::Add((2, 3))); // ❌ Would panic at runtime\n```\n\n### 🔗 Type Relationships\n\nHere's how all the types work together:\n\n```rust\n// 1. Effect declaration generates operation types\neffect! {\n    Console::Print (String) -\u003e ();\n    Math::Add ((i32, i32)) -\u003e i32;\n}\n// Generates: Console, Math, Op enums + From impls\n\n// 2. Effectful functions return Effectful\u003cR, Op\u003e\n#[effectful]\nfn interactive_calculator() -\u003e i32 {           // Returns Effectful\u003ci32, Op\u003e\n    let _: () = perform!(Console::Print(\"Enter numbers...\".to_string()));\n    let result: i32 = perform!(Math::Add((5, 3)));\n    result\n}\n\n// 3. Handlers implement behavior for Op\nstruct MyHandler;\nimpl Handler\u003cOp\u003e for MyHandler { ... }\n\n// 4. Execution ties everything together\nlet computation: Effectful\u003ci32, Op\u003e = interactive_calculator();\nlet handled: Handled\u003ci32, Op, MyHandler\u003e = computation.handle(MyHandler);\nlet result: i32 = handled.run();\n```\n\n### 🧪 Testing Patterns\n\nThe type system makes testing effectful code straightforward:\n\n```rust\n#[effectful]\nfn user_workflow() -\u003e String {\n    let _: () = perform!(Logger::Info(\"Starting workflow\".to_string()));\n    let name: String = perform!(Console::ReadLine);\n    let _: () = perform!(Logger::Info(format!(\"Hello, {}\", name)));\n    name\n}\n\n#[test]\nfn test_user_workflow() {\n    struct TestHandler {\n        input: String,\n        logs: Vec\u003cString\u003e,\n    }\n    \n    impl Handler\u003cOp\u003e for TestHandler {\n        fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n            match op {\n                Op::Console(Console::ReadLine) =\u003e Box::new(self.input.clone()),\n                Op::Logger(Logger::Info(msg)) =\u003e {\n                    self.logs.push(msg.clone());\n                    Box::new(())\n                }\n            }\n        }\n    }\n    \n    let mut handler = TestHandler {\n        input: \"Alice\".to_string(),\n        logs: Vec::new(),\n    };\n    \n    let result = user_workflow().handle(handler).run();\n    assert_eq!(result, \"Alice\");\n    // handler.logs contains the logged messages\n}\n```\n\nThis comprehensive type system ensures that:\n- **Effects are declared once** and used consistently\n- **Handlers provide correct return types** (checked at runtime)\n- **Effectful functions get properly typed results** from effects\n- **Testing is straightforward** with mock handlers\n- **Composition is natural** through the trait system\n\n## 🔗 Theoretical Foundations\n\nAlgae is based on the mathematical theory of algebraic effects and handlers, developed by researchers like Gordon Plotkin and Matija Pretnar.\n\n### One-Shot vs Multi-Shot Effects\n\nAlgae implements **one-shot (linear) algebraic effects**. Understanding this design choice helps explain what algae can and cannot do:\n\n#### One-Shot Effects (What Algae Implements)\n\n- **Single Response**: Each effect operation receives exactly one response\n- **No Continuation Capture**: Computation state is not saved for later reuse\n- **Linear Control Flow**: Effects execute once and continue forward\n- **Simpler Implementation**: Easier to understand, debug, and optimize\n- **Better Performance**: No overhead from capturing and managing continuations\n\n```rust\n// ✅ Supported: Traditional side effects\nperform!(File::Read(\"config.txt\"))     // Read once, get result once\nperform!(Database::Query(\"SELECT...\")) // Query once, get rows once\nperform!(Logger::Info(\"Starting...\"))  // Log once, acknowledge once\n```\n\n#### Multi-Shot Effects (What Algae Does NOT Implement)\n\n- **Multiple Responses**: Effect operations can be resumed multiple times\n- **Continuation Capture**: Computation state is captured and reusable\n- **Non-Linear Control Flow**: Effects can branch, backtrack, or iterate\n- **Complex Implementation**: Requires sophisticated continuation management\n- **Higher Overhead**: Performance cost of capturing and managing state\n\n```rust\n// ❌ Not supported: Non-deterministic, generator-style effects\nperform!(Choice::Select(vec![1,2,3]))  // Cannot try all options\nperform!(Generator::Yield(value))      // Cannot yield multiple values\nperform!(Search::Backtrack)            // Cannot rewind and try alternatives\n```\n\n#### Why One-Shot?\n\n1. **Covers 90% of Use Cases**: File I/O, networking, databases, logging, state management\n2. **Easier to Learn**: Simpler mental model for developers new to algebraic effects\n3. **Better Performance**: No continuation overhead means faster execution\n4. **Reliable**: Fewer edge cases and potential for subtle bugs\n5. **Rust-Friendly**: Aligns well with Rust's ownership model and zero-cost abstractions\n\nFor advanced use cases requiring multi-shot effects (like probabilistic programming, \nnon-deterministic search, or complex generators), consider specialized libraries or\nimplementing custom continuation-passing patterns.\n\n### Mapping to Theory\n\n| **Theory** | **Algae Implementation** | **Purpose** |\n|------------|-------------------------|-------------|\n| **Effect Signature** | `effect!` macro | Declares operations and their types |\n| **Effect Operation** | `perform!(Operation)` | Invokes an effect operation |\n| **Handler** | `Handler\u003cOp\u003e` trait | Provides interpretation for operations |\n| **Computation** | `Effectful\u003cR, Op\u003e` | Computation that may perform effects |\n| **Effectful Function** | `fn f() -\u003e T` with `#[effectful]` → `fn f() -\u003e Effectful\u003cT, Op\u003e` | Function that returns a computation |\n| **Handler Installation** | `.handle(h).run()` | Applies handler to computation |\n\nThe distinction between effectful functions and computations is important:\n- **Effectful function**: `greet_user()` - A pure function that returns a computation\n- **Computation**: `Effectful\u003cString, Op\u003e` - The value returned by the function, representing effects to be performed\n- **Execution**: `greet_user().handle(h).run()` - Running the computation with a handler\n\n\u003e **📁 Theory in Practice**: See [`examples/theory.rs`](algae/examples/theory.rs) for a complete demonstration of how these theoretical concepts map to working code.\n\n### Algebraic Laws\n\nAlgae respects the fundamental algebraic laws of effects:\n\n1. **Associativity**: `(a \u003e\u003e b) \u003e\u003e c ≡ a \u003e\u003e (b \u003e\u003e c)`\n2. **Identity**: Handler for no-op effects acts as identity\n3. **Homomorphism**: Handlers preserve the algebraic structure\n\n\u003e **📁 Laws in Action**: See [`tests/algebraic_laws.rs`](algae/tests/algebraic_laws.rs) for comprehensive tests and educational explanations of all 12 algebraic laws, including beginner-friendly introductions to the mathematical concepts.\n\n### Comparison with Other Approaches\n\n| **Approach** | **Composability** | **Type Safety** | **Performance** | **Testability** |\n|--------------|-------------------|-----------------|-----------------|------------------|\n| **Algebraic Effects** | ✅ Excellent | ✅ Full | ✅ Low-cost | ✅ Excellent |\n| **Async/Await** | ⚠️ Limited | ✅ Good | ✅ Good | ⚠️ Moderate |\n| **Dependency Injection** | ⚠️ Moderate | ⚠️ Runtime | ⚠️ Overhead | ✅ Good |\n| **Global State** | ❌ Poor | ❌ None | ✅ Fast | ❌ Poor |\n\n## 🏗️ Architecture\n\n### Library Structure\n\n```\nalgae/\n├── algae/                 # Core library\n│   ├── src/lib.rs        # Effect, Effectful, Handler types\n│   └── examples/         # Example programs\n├── algae-macros/         # Procedural macros\n│   └── src/lib.rs        # effect!, #[effectful], perform! macros\n└── README.md\n```\n\n### Generated Code\n\nThe `effect!` macro generates:\n\n```rust\n// From this:\neffect! {\n    Console::Print (String) -\u003e ();\n    Console::ReadLine -\u003e String;\n}\n\n// Generates this:\n#[derive(Debug, Clone)]\npub enum Console {\n    Print(String),\n    ReadLine,\n}\n\n#[derive(Debug, Clone)]\npub enum Op {\n    Console(Console),\n}\n\nimpl From\u003cConsole\u003e for Op {\n    fn from(c: Console) -\u003e Op { Op::Console(c) }\n}\n```\n\n### Runtime Behavior\n\n1. **Effectful Function Call**: Returns `Effectful\u003cR, Op\u003e` (zero-cost wrapper)\n2. **Handler Installation**: Creates `Handled\u003cR, Op, H\u003e` (zero-cost wrapper)\n3. **Execution**: Drives coroutine, yielding effects to handler\n4. **Effect Processing**: Handler processes operation, returns typed result\n5. **Resume**: Coroutine resumes with handler's reply\n\n## 🧪 Examples\n\nThe library includes several examples demonstrating different patterns:\n\n### Getting Started Guide\n```bash\ncargo run --example overview\n```\nComprehensive roadmap showing where to find all examples, tests, and documentation.\n\n### Thread Safety Examples\n```bash\ncargo run --example test_send_across_threads\n```\nDemonstrates how effectful computations can be safely sent across threads for concurrent processing.\n\n### Quick Start - README Example\n```bash\ncargo run --example readme\n```\nComplete, runnable version of the README's introductory example with both real and mock handlers.\n\n### Explicit vs Convenient Syntax\n```bash\ncargo run --example explicit_vs_convenient\n```\nSide-by-side demonstration showing that `#[effectful]` and `perform!` are pure convenience macros that generate identical code to the explicit approach.\n\n### Multiple Effects Patterns\n```bash\ncargo run --example multiple_effects_demo\n```\nComprehensive guide to organizing multiple effects: single declaration vs module separation, with trade-offs and best practices.\n\n### Custom Root Effects  \n```bash\ncargo run --example custom_root_effects\n```\nDemonstrates the new custom root enum functionality: avoiding conflicts, combining roots, and managing multiple effect declarations in one module.\n\n### Advanced Patterns\n```bash\ncargo run --example advanced\n```\nComplex multi-effect application with file I/O, database operations, logging, error handling, and comprehensive testing patterns.\n\n### Theoretical Foundations\n```bash\ncargo run --example theory\n```\nDemonstrates the mapping between algebraic effects theory and algae implementation, including algebraic laws.\n\n### State Management\n```bash\ncargo run --example pure\n```\nShows pure functional state management using algebraic effects.\n\n### Interactive I/O\n```bash\ncargo run --example console\n```\nDemonstrates interactive I/O with both real and mock implementations, plus random number generation.\n\n### Partial Handlers (Panic-Free Composition)\n```bash\ncargo run --example partial_handlers\n```\nShows how to use partial handlers for safe, modular effect composition without panics.\n\n### Basic Functionality\n```bash\ncargo run --example effect_test\n```\nBasic test of the effect system with simple operations.\n\n### Low-Level Coroutines\n```bash\ncargo run --example minimal\n```\nMinimal example showing the underlying coroutine mechanics (educational).\n\n### No-Macros Usage\n```bash\ncargo run --example no_macros --no-default-features\n```\nComplete example showing how to use algae without any macros - pure explicit syntax.\n\n### Run All Examples\n```bash\n# Run core examples demonstrating main features\nfor example in readme explicit_vs_convenient multiple_effects_demo test_send_across_threads advanced theory pure console partial_handlers variable_handler_chain chained_handlers effect_test minimal; do\n    echo \"=== Running $example ===\"\n    cargo run --example $example\n    echo\ndone\n\n# Run test examples demonstrating bug fixes and edge cases\nfor example in test_non_default_payload test_custom_root_effectful test_effectful_scoping_fix test_error_messages; do\n    echo \"=== Running $example ===\"\n    cargo run --example $example\n    echo\ndone\n\n# Run no-macros example separately (requires different feature flags)\necho \"=== Running no_macros ===\"\ncargo run --example no_macros --no-default-features\n```\n\n## 🔧 Development\n\n### Prerequisites\n\n- **Rust Nightly**: Required for coroutine features\n- **Git**: For cloning the repository\n\n### Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/your-username/algae.git\ncd algae\n\n# Ensure you're using nightly Rust\nrustup default nightly\n\n# Or set up a toolchain file (already included)\ncat rust-toolchain.toml\n```\n\n### Building\n\n```bash\n# Build the library\ncargo build\n\n# Build with optimizations\ncargo build --release\n\n# Build documentation\ncargo doc --open\n```\n\n### Testing\n\n```bash\n# Run all tests\ncargo test\n\n# Run only unit tests\ncargo test --lib\n\n# Run only integration tests  \ncargo test --test '*'\n\n# Run only documentation tests\ncargo test --doc\n\n# Run with verbose output\ncargo test -- --nocapture\n```\n\n### Code Quality\n\n```bash\n# Check for issues\ncargo clippy --all-targets -- -D warnings\n\n# Format code\ncargo fmt\n\n# Check formatting\ncargo fmt -- --check\n```\n\n### Examples\n\n```bash\n# Run core examples\ncargo run --example pure\ncargo run --example console  \ncargo run --example debug\ncargo run --example effect_test\ncargo run --example test_send_across_threads\n\n# Run feature demonstrations\ncargo run --example test_non_default_payload\ncargo run --example test_custom_root_effectful\ncargo run --example test_error_messages\n\n# Run specific example with release optimizations\ncargo run --release --example console\n```\n\n### Benchmarking\n\n```bash\n# Run benchmarks (if implemented)\ncargo bench\n\n# Profile memory usage\ncargo run --example pure --features profiling\n```\n\n## 🎛️ Optional Macros Feature\n\nAlgae's macros (`effect!`, `#[effectful]`, `perform!`) are **optional**. You can disable them if you prefer explicit syntax or have restrictions on proc-macros.\n\n### Default: Macros Enabled\n\n```toml\n[dependencies]\nalgae = \"0.1.0\"  # macros feature enabled by default\n```\n\n### Disable Macros\n\n```toml\n[dependencies]\nalgae = { version = \"0.1.0\", default-features = false }\n```\n\n### No-Macros Example\n\nWhen macros are disabled, you define everything manually:\n\n```rust\n#![feature(coroutines, coroutine_trait, yield_expr)]\nuse algae::prelude::*;  // Only exports core types, no macros\nuse std::any::Any;\n\n// 1. Manually define effect enums (instead of effect! macro)\n#[derive(Debug)]\npub enum Console {\n    Print(String),\n    ReadLine,\n}\n\n#[derive(Debug)]  \npub enum Op {\n    Console(Console),\n}\n\nimpl From\u003cConsole\u003e for Op {\n    fn from(c: Console) -\u003e Self {\n        Op::Console(c)\n    }\n}\n\n// 2. Manually create effectful functions (instead of #[effectful])\nfn greet_user() -\u003e Effectful\u003cString, Op\u003e {\n    Effectful::new(#[coroutine] |mut _reply: Option\u003cReply\u003e| {\n        // Manual effect operations (instead of perform!)\n        {\n            let effect = Effect::new(Console::Print(\"What's your name?\".to_string()).into());\n            let reply_opt = yield effect;\n            let _: () = reply_opt.unwrap().take::\u003c()\u003e();\n        }\n        \n        let name: String = {\n            let effect = Effect::new(Console::ReadLine.into());\n            let reply_opt = yield effect;\n            reply_opt.unwrap().take::\u003cString\u003e()\n        };\n        \n        format!(\"Hello, {}!\", name)\n    })\n}\n\n// 3. Handlers work exactly the same\nstruct ConsoleHandler;\nimpl Handler\u003cOp\u003e for ConsoleHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn Any + Send\u003e {\n        match op {\n            Op::Console(Console::Print(msg)) =\u003e {\n                println!(\"{}\", msg);\n                Box::new(())\n            }\n            Op::Console(Console::ReadLine) =\u003e {\n                // In real code, read from stdin\n                Box::new(\"Alice\".to_string())\n            }\n        }\n    }\n}\n\n// 4. Execution is identical\nfn main() {\n    let result = greet_user()\n        .handle(ConsoleHandler)\n        .run();\n    println!(\"Result: {}\", result);\n}\n```\n\n\u003e **📁 Working Example**: See [`examples/no_macros.rs`](algae/examples/no_macros.rs) for a complete working example without macros.\n\n### When to Disable Macros\n\n**Use the manual approach when:**\n- **Proc-macro restrictions**: Your environment doesn't allow procedural macros\n- **Full control**: You need custom implementations of the generated types\n- **Library development**: Minimizing dependencies for a library crate\n- **Learning**: Understanding exactly how the effects system works\n- **Custom syntax**: Building your own effect DSL on top of algae\n\n**Use macros (default) when:**\n- **Productivity**: You want clean, readable application code\n- **Rapid development**: Prototyping or building applications quickly\n- **Standard use cases**: The generated code meets your needs\n- **Team development**: Consistent, familiar syntax for all developers\n\n### Feature Compatibility\n\nBoth approaches provide identical capabilities:\n- ✅ **One-shot algebraic effects** - Same runtime model\n- ✅ **Type-safe effect handlers** - Same type system\n- ✅ **Composable effect systems** - Same composition patterns\n- ✅ **Zero-cost abstractions** - Same performance characteristics\n- ✅ **Full coroutine support** - Same underlying implementation\n\n**The only difference is syntax for defining and using effects.**\n\n## 📖 Advanced Usage\n\n### Multiple Effect Families\n\n#### ✅ Recommended: Single `effect!` Declaration\n\nYou can define multiple effect families in a single declaration:\n\n```rust\neffect! {\n    // File operations\n    File::Read (String) -\u003e Result\u003cString, std::io::Error\u003e;\n    File::Write ((String, String)) -\u003e Result\u003c(), std::io::Error\u003e;\n    \n    // Network operations  \n    Http::Get (String) -\u003e Result\u003cString, reqwest::Error\u003e;\n    Http::Post ((String, String)) -\u003e Result\u003cString, reqwest::Error\u003e;\n    \n    // Database operations\n    Db::Query (String) -\u003e Vec\u003cRow\u003e;\n    Db::Execute (String) -\u003e Result\u003cu64, DbError\u003e;\n    \n    // Logging operations\n    Logger::Info (String) -\u003e ();\n    Logger::Error (String) -\u003e ();\n}\n```\n\nThis generates a single `Op` enum that contains all your effect families:\n\n```rust\n// Generated by the macro\npub enum Op {\n    File(File),\n    Http(Http), \n    Db(Db),\n    Logger(Logger),\n}\n```\n\n#### ✅ Custom Root Enum Names\n\n##### Overview\n\nWhen building larger applications, you may need to define effects in different modules or avoid naming conflicts between different effect families. Algae provides custom root enum names to solve this problem elegantly.\n\n##### **Custom root enums**: Use `effect! { root CustomOp; ... }` to avoid naming conflicts\n\nBy default, the `effect!` macro generates a root enum called `Op`. However, when you need multiple effect declarations in the same scope, you can specify a custom root enum name:\n\n```rust\n// Instead of the default Op enum, use ConsoleOp\neffect! {\n    root ConsoleOp;\n    Console::Print (String) -\u003e ();\n    Console::ReadLine -\u003e String;\n}\n\n// This generates:\n// - enum Console { Print(String), ReadLine }\n// - enum ConsoleOp { Console(Console) }  // Custom root instead of Op\n// - impl From\u003cConsole\u003e for ConsoleOp { ... }\n```\n\nThis feature is essential when:\n- Building modular effect systems\n- Avoiding naming conflicts in large codebases\n- Creating reusable effect libraries\n- Separating concerns between different domains\n\n##### **Flexible attributes**: `#[effectful(root = CustomOp)]` works with custom root types\n\nThe `#[effectful]` attribute macro seamlessly adapts to your custom root types:\n\n```rust\neffect! {\n    root FileSystemOp;\n    FS::Read (String) -\u003e Result\u003cString, std::io::Error\u003e;\n    FS::Write ((String, String)) -\u003e Result\u003c(), std::io::Error\u003e;\n}\n\n// The #[effectful] macro automatically uses FileSystemOp\n#[effectful(root = FileSystemOp)]\nfn process_config(path: String) -\u003e Result\u003cString, std::io::Error\u003e {\n    let content: Result\u003cString, std::io::Error\u003e = perform!(FS::Read(path.clone()));\n    let content = content?;\n    \n    let processed = content.to_uppercase();\n    let _: Result\u003c(), std::io::Error\u003e = perform!(FS::Write((\n        format!(\"{}.processed\", path),\n        processed.clone()\n    )))?;\n    \n    Ok(processed)\n}\n// Returns: Effectful\u003cResult\u003cString, std::io::Error\u003e, FileSystemOp\u003e\n```\n\nKey points about `#[effectful(root = CustomOp)]`:\n- **Automatic type inference**: The macro determines the correct root type\n- **Type safety**: Compile-time verification that effects match the root\n- **Seamless integration**: Works identically to the default `Op` case\n- **Handler compatibility**: Handlers implement `Handler\u003cCustomOp\u003e` instead of `Handler\u003cOp\u003e`\n\n##### **Multiple effect families**: Organize large codebases with modular effect declarations\n\nCustom root enums enable sophisticated architectural patterns for large applications:\n\n```rust\n// Domain-specific effect families\neffect! {\n    root AuthOp;\n    Auth::Login ((String, String)) -\u003e Result\u003cUser, AuthError\u003e;\n    Auth::Logout -\u003e ();\n    Auth::CheckPermission (Permission) -\u003e bool;\n}\n\neffect! {\n    root DataOp;\n    Db::Query (String) -\u003e Vec\u003cRow\u003e;\n    Db::Execute (String) -\u003e Result\u003cu64, DbError\u003e;\n    Cache::Get (String) -\u003e Option\u003cString\u003e;\n    Cache::Set ((String, String)) -\u003e ();\n}\n\neffect! {\n    root BusinessOp;\n    Order::Create (OrderRequest) -\u003e Result\u003cOrder, BusinessError\u003e;\n    Order::Process (OrderId) -\u003e Result\u003c(), BusinessError\u003e;\n    Inventory::Check (ProductId) -\u003e u32;\n    Inventory::Reserve ((ProductId, u32)) -\u003e Result\u003c(), BusinessError\u003e;\n}\n\n// Combine all effects for the application\nalgae::combine_roots!(pub AppOp = AuthOp, DataOp, BusinessOp);\n\n// Now you can write handlers that compose different domains\nstruct AppHandler {\n    auth: AuthHandler,\n    data: DataHandler,\n    business: BusinessHandler,\n}\n\nimpl Handler\u003cAppOp\u003e for AppHandler {\n    fn handle(\u0026mut self, op: \u0026AppOp) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            AppOp::AuthOp(auth_op) =\u003e self.auth.handle(auth_op),\n            AppOp::DataOp(data_op) =\u003e self.data.handle(data_op),\n            AppOp::BusinessOp(business_op) =\u003e self.business.handle(business_op),\n        }\n    }\n}\n\n// Functions can use any combination of effects\n#[effectful(root = AppOp)]\nfn place_order(user_id: UserId, request: OrderRequest) -\u003e Result\u003cOrder, String\u003e {\n    // Check authentication\n    let has_permission: bool = perform!(Auth::CheckPermission(Permission::CreateOrder).into());\n    if !has_permission {\n        return Err(\"Insufficient permissions\".to_string());\n    }\n    \n    // Check inventory\n    let available: u32 = perform!(Inventory::Check(request.product_id).into());\n    if available \u003c request.quantity {\n        return Err(\"Insufficient inventory\".to_string());\n    }\n    \n    // Reserve inventory\n    let _: Result\u003c(), BusinessError\u003e = perform!(Inventory::Reserve((\n        request.product_id,\n        request.quantity\n    )).into()).map_err(|e| e.to_string())?;\n    \n    // Create order\n    let order: Result\u003cOrder, BusinessError\u003e = perform!(Order::Create(request).into());\n    order.map_err(|e| e.to_string())\n}\n```\n\nBenefits of this approach:\n- **Clear separation**: Each domain has its own effect family\n- **Type safety**: Effects are grouped logically\n- **Modular testing**: Test each domain independently\n- **Team scalability**: Different teams can work on different effect families\n- **Incremental adoption**: Add new effect families without touching existing code\n\nYou can use multiple `effect!` declarations in the same module by specifying custom root enum names:\n\n```rust\n// ✅ Works with custom root names\neffect! {\n    root ConsoleOp;\n    Console::Print (String) -\u003e ();\n    Console::ReadLine -\u003e String;\n}\n\neffect! {\n    root FileOp;\n    File::Read (String) -\u003e Result\u003cString, String\u003e;\n    File::Write ((String, String)) -\u003e Result\u003c(), String\u003e;\n}\n\neffect! {\n    root NetworkOp;\n    Http::Get (String) -\u003e Result\u003cString, String\u003e;\n    Http::Post ((String, String)) -\u003e Result\u003cString, String\u003e;\n}\n```\n\nEach generates its own root enum:\n- `ConsoleOp` containing `Console` variants\n- `FileOp` containing `File` variants  \n- `NetworkOp` containing `Http` variants\n\nYou can then combine them using the `combine_roots!` macro:\n\n```rust\n// Combine multiple root enums into one\nalgae::combine_roots!(pub Op = ConsoleOp, FileOp, NetworkOp);\n\n// Now you can write unified handlers\nimpl Handler\u003cOp\u003e for UnifiedHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            Op::ConsoleOp(console_op) =\u003e self.console_handler.handle(console_op),\n            Op::FileOp(file_op) =\u003e self.file_handler.handle(file_op),\n            Op::NetworkOp(network_op) =\u003e self.network_handler.handle(network_op),\n        }\n    }\n}\n```\n\n#### ❌ Error Detection: Duplicate Root Names\n\nAttempting to use duplicate root names (including the default `Op`) in the same scope will produce clear error messages:\n\n```rust\n// ❌ ERROR: Conflicting Op enum definitions\neffect! {\n    Console::Print (String) -\u003e ();\n}\n\neffect! {\n    Math::Add ((i32, i32)) -\u003e i32;\n}\n// Error: duplicate definition of `Op`\n```\n\nEach `effect!` macro generates its own `Op` enum, so multiple declarations in the same scope create conflicting type definitions.\n\n#### ✅ Alternative: Module-Based Separation\n\nFor large codebases, you can separate effects into modules:\n\n```rust\nmod console_effects {\n    use algae::prelude::*;\n    \n    effect! {\n        Console::Print (String) -\u003e ();\n        Console::ReadLine -\u003e String;\n    }\n    \n    #[effectful]\n    pub fn interactive_session() -\u003e String {\n        let _: () = perform!(Console::Print(\"Hello!\".to_string()));\n        let name: String = perform!(Console::ReadLine);\n        name\n    }\n}\n\nmod math_effects {\n    use algae::prelude::*;\n    \n    effect! {\n        Math::Add ((i32, i32)) -\u003e i32;\n        Math::Multiply ((i32, i32)) -\u003e i32;\n    }\n    \n    #[effectful] \n    pub fn calculation(x: i32, y: i32) -\u003e i32 {\n        let sum: i32 = perform!(Math::Add((x, y)));\n        perform!(Math::Multiply((sum, 2)))\n    }\n}\n```\n\n**Trade-offs of module separation:**\n- ✅ **Good for**: Large teams, feature boundaries, independent testing\n- ❌ **Limitation**: Can't easily compose effects across modules\n- ❌ **Complexity**: Each module needs its own handler\n\n#### 📁 When to Use Each Approach\n\n| **Single `effect!`** | **Module Separation** |\n|---------------------|----------------------|\n| ✅ Small to medium projects | ✅ Large codebases with teams |\n| ✅ Effects that interact | ✅ Independent feature areas |\n| ✅ Single unified handler | ✅ Separate testing strategies |\n| ✅ Easy composition | ❌ Complex cross-module composition |\n\n\u003e **📁 Working Example**: See [`examples/multiple_effects_demo.rs`](algae/examples/multiple_effects_demo.rs) for complete demonstrations of both patterns.\n\n### Handler Composition\n\nHandlers can be composed to handle different effect families:\n\n```rust\nstruct CompositeHandler {\n    file_handler: FileHandler,\n    http_handler: HttpHandler, \n    db_handler: DbHandler,\n}\n\nimpl Handler\u003cOp\u003e for CompositeHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn std::any::Any + Send\u003e {\n        match op {\n            Op::File(_) =\u003e self.file_handler.handle(op),\n            Op::Http(_) =\u003e self.http_handler.handle(op),\n            Op::Db(_) =\u003e self.db_handler.handle(op),\n        }\n    }\n}\n```\n\n### Error Handling Patterns\n\nEffects naturally support `Result` types for error handling:\n\n```rust\n#[effectful]\nfn safe_file_operation(path: String) -\u003e Result\u003cString, AppError\u003e {\n    let content: Result\u003cString, std::io::Error\u003e = perform!(File::Read(path.clone()));\n    let content = content.map_err(AppError::IoError)?;\n    \n    let result: Result\u003c(), std::io::Error\u003e = perform!(File::Write((\n        format!(\"{}.backup\", path),\n        content.clone()\n    )));\n    result.map_err(AppError::IoError)?;\n    \n    Ok(content)\n}\n```\n\n### Control Flow\n\nEffectful functions support all Rust control flow:\n\n```rust\n#[effectful]\nfn batch_process(items: Vec\u003cString\u003e) -\u003e Vec\u003cResult\u003cString, String\u003e\u003e {\n    let mut results = Vec::new();\n    \n    for (i, item) in items.iter().enumerate() {\n        let _: () = perform!(Logger::Info(format!(\"Processing item {}: {}\", i, item)));\n        \n        let result = match item.as_str() {\n            \"skip\" =\u003e {\n                let _: () = perform!(Logger::Info(\"Skipping item\".to_string()));\n                continue;\n            }\n            \"break\" =\u003e {\n                let _: () = perform!(Logger::Info(\"Breaking early\".to_string())); \n                break;\n            }\n            _ =\u003e {\n                let processed: Result\u003cString, String\u003e = perform!(Processor::Handle(item.clone()));\n                processed\n            }\n        };\n        \n        results.push(result);\n    }\n    \n    results\n}\n```\n\n### Partial Handlers and Safe Composition\n\nAlgae supports **partial handlers** that can selectively handle operations, enabling safe effect composition without panics.\n\n#### Variable-Length Handler Chains\n\nThe library now supports chaining an arbitrary number of handlers together:\n\n```rust\n// Using handle_all to attach multiple handlers at once\nlet result = computation()\n    .handle_all(vec![\n        Box::new(ConsoleHandler),\n        Box::new(FileHandler),\n        Box::new(LoggerHandler),\n    ])\n    .run_checked()?;\n\n// Chaining handlers one by one\nlet result = computation()\n    .begin_chain()          // Start with empty VecHandler\n    .handle(ConsoleHandler)\n    .handle(FileHandler)\n    .handle(LoggerHandler)\n    .run_checked()?;\n\n// Starting with one handler and adding more\nlet result = computation()\n    .handle_all([ConsoleHandler])  // Start with one\n    .handle(FileHandler)           // Add another\n    .handle(LoggerHandler)         // And another\n    .run_checked()?;\n\n// Building handler chain dynamically\nlet mut handled = computation().begin_chain().handle(ConsoleHandler);\nif need_file_ops {\n    handled = handled.handle(FileHandler);\n}\nif need_logging {\n    handled = handled.handle(LoggerHandler);\n}\nlet result = handled.run_checked()?;\n\n// Or build a VecHandler manually for more control\nlet mut handlers = VecHandler::new();\nhandlers.push(ConsoleHandler);\nhandlers.push(FileHandler);\nhandlers.push(LoggerHandler);\n\nlet result = computation().run_checked(handlers)?;\n```\n\n#### Handler Types\n\n```rust\n// Define partial handlers that only handle specific operations\nstruct MathHandler;\nimpl PartialHandler\u003cOp\u003e for MathHandler {\n    fn maybe_handle(\u0026mut self, op: \u0026Op) -\u003e Option\u003cBox\u003cdyn std::any::Any + Send\u003e\u003e {\n        match op {\n            Op::Math(Math::Add((a, b))) =\u003e Some(Box::new(a + b)),\n            Op::Math(Math::Multiply((a, b))) =\u003e Some(Box::new(a * b)),\n            _ =\u003e None,  // Decline other operations\n        }\n    }\n}\n\nstruct LoggerHandler;\nimpl PartialHandler\u003cOp\u003e for LoggerHandler {\n    fn maybe_handle(\u0026mut self, op: \u0026Op) -\u003e Option\u003cBox\u003cdyn std::any::Any + Send\u003e\u003e {\n        match op {\n            Op::Logger(Logger::Info(msg)) =\u003e {\n                println!(\"[INFO] {}\", msg);\n                Some(Box::new(()))\n            }\n            _ =\u003e None,\n        }\n    }\n}\n\n// Compose handlers and get Result-based error handling\n#[effectful]\nfn program() -\u003e i32 {\n    let _: () = perform!(Logger::Info(\"Starting calculation\".to_string()));\n    let sum: i32 = perform!(Math::Add((2, 3)));\n    let _: () = perform!(Logger::Info(format!(\"Result: {}\", sum)));\n    sum\n}\n\n// Method 1: Manual VecHandler\nlet mut handlers = VecHandler::new();\nhandlers.push(MathHandler);\nhandlers.push(LoggerHandler);\n\nmatch program().run_checked(handlers) {\n    Ok(result) =\u003e println!(\"Success: {}\", result),\n    Err(UnhandledOp(op)) =\u003e eprintln!(\"Unhandled operation: {:?}\", op),\n}\n\n// Method 2: Using handle_all\nlet result = program()\n    .handle_all(vec![\n        Box::new(MathHandler) as Box\u003cdyn PartialHandler\u003cOp\u003e + Send\u003e,\n        Box::new(LoggerHandler),\n    ])\n    .run_checked()?;\n```\n\n#### Key Benefits\n\n- **🔒 No Panics**: `run_checked` returns `Result\u003cT, UnhandledOp\u003cOp\u003e\u003e` instead of panicking\n- **🔄 Composable**: Combine multiple handlers that each handle a subset of operations\n- **📦 Modular**: Handlers can be developed and tested independently\n- **🎯 Clear Errors**: Know exactly which operation wasn't handled\n- **⚡ Same Performance**: No additional overhead compared to total handlers\n\n#### Handler Types\n\n```rust\n// Total handler (existing) - must handle all operations\nimpl Handler\u003cOp\u003e for TotalHandler {\n    fn handle(\u0026mut self, op: \u0026Op) -\u003e Box\u003cdyn Any + Send\u003e {\n        match op {\n            // Must handle ALL operations or panic\n        }\n    }\n}\n\n// Partial handler (new) - can decline operations\nimpl PartialHandler\u003cOp\u003e for SelectiveHandler {\n    fn maybe_handle(\u0026mut self, op: \u0026Op) -\u003e Option\u003cBox\u003cdyn Any + Send\u003e\u003e {\n        match op {\n            // Return Some for handled operations\n            // Return None to decline\n        }\n    }\n}\n\n// Total handlers can still be used with run_checked_with\nlet result = computation.run_checked_with(TotalHandler)?;\n```\n\n\u003e **📁 Working Examples**: \n\u003e - [`examples/partial_handlers.rs`](algae/examples/partial_handlers.rs) - Comprehensive demonstrations of partial handlers\n\u003e - [`examples/variable_handler_chain.rs`](algae/examples/variable_handler_chain.rs) - Variable-length handler chains with zero-panic execution\n\u003e - [`examples/chained_handlers.rs`](algae/examples/chained_handlers.rs) - Demonstrates the `.handle().handle().handle()` chaining syntax\n\u003e - [`examples/clean_chaining.rs`](algae/examples/clean_chaining.rs) - Simplest handler chaining with `.begin_chain()`\n\n## 🔬 Performance\n\n### Benchmarks\n\nAlgae is designed for minimal runtime overhead:\n\n- **Effect Declaration**: Compile-time only, no runtime cost\n- **Effectful Functions**: Single heap allocation for coroutine state machine\n- **Handler Calls**: Static dispatch with dynamic typing for return values\n- **Type Safety**: Compile-time checked effects, runtime type verification for replies\n- **Performance Cost**: Comparable to `async/await` but with more flexibility\n\n### Memory Usage\n\n- **Single Allocation per Computation**: One heap allocation for the coroutine state\n- **Stack-Safe**: Uses coroutines instead of recursion for deep effect chains\n- **No GC Pressure**: All allocations are explicit and bounded\n- **Dynamic Typing Overhead**: `Box\u003cdyn Any + Send\u003e` for handler return values\n- **Thread Safety**: Send trait enables zero-cost transfer between threads\n\n### Performance Considerations\n\n**Costs:**\n- One heap allocation per effectful computation (for coroutine state)\n- Dynamic type checking when extracting handler replies (`Reply::take()`)\n- Coroutine suspend/resume overhead (similar to async/await)\n- Pattern matching on effect operations\n\n**Optimizations:**\n1. **Minimize effect frequency**: Batch operations when possible\n2. **Use concrete handler types**: Avoid trait objects where possible  \n3. **Profile critical paths**: Effects add overhead to hot loops\n4. **Consider alternatives**: For tight loops, direct function calls may be faster\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see our contributing guidelines:\n\n### Getting Started\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b my-feature`\n3. Make your changes\n4. Add tests for new functionality\n5. Ensure all tests pass: `cargo test`\n6. Run clippy: `cargo clippy --all-targets -- -D warnings`\n7. Format your code: `cargo fmt`\n8. Submit a pull request\n\n### Development Tools\n\nThe project includes a comprehensive Makefile for development:\n\n```bash\n# Quick development workflow\nmake dev              # Format, check, and test\nmake ci-local         # Run full CI pipeline locally\nmake examples         # Check all examples compile\nmake test-error-detection  # Verify error cases work correctly\n\n# Individual tasks\nmake test             # Run all tests\nmake clippy           # Run linting (matches CI)\nmake fmt              # Format code\nmake doc              # Build documentation\n```\n\n### Areas for Contribution\n\n- **Documentation**: Improve examples and guides\n- **Performance**: Benchmarks and optimizations\n- **Testing**: Additional test cases and property tests\n- **Examples**: Real-world usage examples\n- **Integrations**: Async/await compatibility, tokio integration\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🙏 Acknowledgments\n\n- **Gordon Plotkin** and **Matija Pretnar** for the theoretical foundations of algebraic effects\n- **The Rust Community** for excellent tools and ecosystem\n- **OCaml's Effects** for inspiration on practical algebraic effects\n- **Koka Language** for demonstrating effect types in systems programming\n- **Eff Language** for the original algebraic effects implementation\n\n## 📚 Further Reading\n\n### Academic Papers\n- [Algebraic Effects and Handlers](https://www.eff-lang.org/handlers-tutorial.pdf) - Tutorial introduction\n- [An Introduction to Algebraic Effects and Handlers](https://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/handlers.pdf) - Comprehensive overview\n- [Handling Asynchronous Exceptions with Algebraic Effects](https://arxiv.org/abs/1310.3981) - Advanced applications\n\n### Other Implementations\n- [Eff Language](https://www.eff-lang.org/) - The original algebraic effects language\n- [Koka](https://koka-lang.github.io/) - Microsoft's research language with effect types  \n- [OCaml 5.0 Effects](https://ocaml.org/manual/effects.html) - Effects in OCaml\n- [Unison](https://www.unison-lang.org/) - Functional language with algebraic effects\n\n### Blog Posts and Tutorials\n- [Algebraic Effects for the Rest of Us](https://overreacted.io/algebraic-effects-for-the-rest-of-us/) - Accessible introduction\n- [Effects in Rust](https://boats.gitlab.io/blog/post/await-decision/) - Rust-specific discussions\n- [What are Algebraic Effects?](https://jrsinclair.com/articles/2019/algebraic-effects-what-are-they/) - Practical explanation\n\n---\n\n**Built with ❤️ and Rust 🦀**","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdashed%2Falgae","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdashed%2Falgae","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdashed%2Falgae/lists"}