···11# Abort
2233-`Abort[V, E](E)` aborts a computation of `V` with `E`.
33+`Abort<V, E>(E)` aborts a computation of type `V` with an error `E`.
4455-When aborted, a computation is *halted*, meaning that any `Map` or `FlatMap` operation over it will not be executed.
55+When aborted, a computation is halted: any `map` or `flat_map` operation over it will not be executed.
6677-The `AbortHandler` replaces aborted effects (those computations where Abort was called) with a `Result` with `E`. Otherwise if the effect was never aborted, a `Result` with `V` is returned.
77+The `AbortHandler` replaces aborted effects (where `Abort` was called) with a `Result<V, E>`. If the effect was never aborted, a `Result<V, E>` is returned.
8899-- Implementation
1010- - [abort.go](https://github.com/vic/fx.go/blob/main/abort/abort.go)
1111- - [result.go](https://github.com/vic/fx.go/blob/main/abort/result.go)
1212-- Tests [abort_test.go](https://github.com/vic/fx.go/blob/main/abort/abort_test.go)
99+See the implementation and tests in the fx-rs codebase for details.
+33-36
book/src/basics.md
···11-# Basic Effects on `Fx.go`
11+# Basic Effects in `fx-rs`
2233-This section expands on [Concepts](concepts.html) and shows how they relate to the `Fx.go` API, as well as providing a basic intuition on Effect requirements and evaluation.
33+This section expands on [Concepts](concepts.html) and shows how they relate to the `fx-rs` API, providing intuition on effect requirements and evaluation in Rust.
4455-## `Pure[V]`: Immediate Effects
55+## `Fx<(), V>`: Immediate Effects
6677-The most basic of all possible effects are *Immediate* effects. These are effects of type `Fx[Nil, V]`, meaning that they have no ability requirements (`Nil`) and evaluate to a value (`V`).
77+The most basic effects are *immediate* effects. These are of type `Fx<(), V>`, meaning they have no ability requirements and evaluate to a value `V`.
8899-Immediate effects are created using the `Pure(V) Fx[Nil, V]` function.
99+Immediate effects are created using `Fx::pure(value)` or `Fx::value(value)`.
1010+1111+A pure effect just holds an already known value instead of computing it.
10121111-As you can see, `Pure(V)` takes an *existing* value `V`, that means that a pure effect just holds an already known value instead of trying to compute it.
1313+The value can be retrieved by calling `.eval()` on the effect. Only effects with no requirements (`()`) can be evaluated directly.
12141313-The value given to `Pure` can be retrieved by using `Eval(Fx[Nil, V]) V`. Only effects that have no requirements (`Nil`) can be evaluated.
1515+```rust
1616+use fx::Fx;
14171515-```go
1616-import ( fx "github.com/vic/fx.go" )
1818+// Type alias for a simple effect with no requirements
1919+type PureString = Fx<(), String>;
17201818-func PureExample() {
1919- v := "Hello World"
2020- // Code annotated with types for clarity
2121- var effect fx.Fx[fx.Nil, string] = fx.Pure(v)
2222- var result string = fx.Eval(effect)
2323- assert(result == v)
2121+let v = "Hello World".to_owned();
2222+let effect: PureString = Fx::pure(v.clone());
2323+let result: String = effect.eval();
2424+assert_eq!(result, v); // result: String
2425}
2526```
26272727-## `Fx[S, V]`: Pending effects
2828+## `Fx<S, V>`: Pending Effects
28292929-An effect `Fx[S, V]` where `S != Nil` is a pending effect that needs `S` to be _provided_ for computing `V`.
3030+An effect `Fx<S, V>` where `S` is not `()` is a pending effect that needs `S` to be provided before computing `V`.
30313131-The most basic pending computation is one you are already familiar with: *A function*.
3232+The most basic pending computation is a function. For example, a function from `String` to `usize` can be expressed as an effect of type `Fx<String, usize>`:
32333333-In the following example, the function `LengthOfString(string) int` can be expressed as an Effect of type `Fx[string, int]`. Meaning that in order to have a value of `int` you need first to provide an `string` value:
3434+```rust
3535+use fx::Fx;
34363535-```go
3636-func LengthOfString(s string) int {
3737- return len(s)
3737+fn length_of_string(s: String) -> usize {
3838+ s.len()
3839}
39404040-func FuncExample() {
4141- // Code annotated with types for clarity
4242- var effect fx.Fx[string, int] = fx.Func(LengthOfString)
4343- var requirement string = "Hello World"
4444- var provided fx.Fx[fx.Nil, int] = fx.Provide(effect, requirement)
4545- var result int = fx.Eval(provided)
4646- assert(result == len(requirement))
4141+fn func_example() {
4242+ let effect: Fx<String, usize> = Fx::func(length_of_string);
4343+ let requirement = "Hello World".to_owned();
4444+ let provided = effect.provide(requirement.clone());
4545+ let result = provided.eval();
4646+ assert_eq!(result, requirement.len());
4747}
4848```
49495050-From the code above:
5050+- `Fx::func(f)` produces a pending effect of type `Fx<S, V>` from a function `f: S -> V`.
5151+- `.provide(value)` discharges the requirement and returns `Fx<(), V>`. No computation is performed until `.eval()` is called.
5252+- `.eval()` performs the computation, since all requirements have been provided.
51535252-- `Func(func (S) V)` produces a _pending_ effect of type `Fx[S, V]`.
5353-- `Provide(Fx[S, V], S)` discharges the `S` requirement and returns `Fx[Nil, V]`.\
5454- Note that *no computation* is performed in this step. `Fx[Nil, V]` is still a description of a program, and `V` has not been computed yet, nor any side-effect has been performed.
5555-- `Eval(Fx[Nil, V])` will actually perform the computation of `V`. Since all `non-Nil` requirements have already been provided, the computation can be run.
5656-5757-These two are the most basic effects in `Fx.go`. More interesting effects will be presented as we explore the topics of effect Rquests and Handlers.
5454+These are the most basic effects in `fx-rs`. More interesting effects are presented in later chapters.
+17-20
book/src/concepts.md
···11# Concepts Tour
2233-This section will try to introduce you to the concepts of
44-_Effects_, _Abilities_ and _Handlers_ as present in `Fx.go`.
33+This section introduces the concepts of _Effects_, _Abilities_, and _Handlers_ as present in `fx-rs`.
5466-No knowledge or previous experience with other effect sytems
77-is expected. We will try to explain things by
88-working out from simple concepts to more interesting ones.
55+No prior experience with other effect systems is required. We explain concepts from simple to advanced, using Rust idioms.
96107## Effects
1181212-An *Effect* ( `Fx[S, V]` read: `V` provided `S` ) is the _description_ of a program that computes `V`, *provided* that the requirement `S` is present, so that the computation of `V` can be performed.
99+An *Effect* (`Fx<S, V>`, read: "V given S") is a _description_ of a program that computes `V`, provided that the requirement `S` is present.
13101414-Since effects are *description*s of programs, they compute nothing nor produce side-effects until they are finally evaluated, once all their requirements are at met.
1111+Effects are descriptions of programs; they compute nothing and produce no side-effects until they are evaluated, once all requirements are met.
15121616-Some people also use the *recipe* analogy for effects: you first have a precise description of each step it takes to cook something, along with a list of the requirements for it (the ingredients and utencils you will use) and once you have them all, you can actually perform the recipe.
1313+A common analogy is a recipe: you have a description of steps and a list of requirements (ingredients and utensils). Once you have them all, you can perform the recipe.
17141815## Abilities
19162020-In `Fx[S, V]`, `S` is said to be the *Ability* (sometimes also referred as the _set_ of *Abilities*, *Capabilities*, *Effect Environment* or *Effect Requirements*) that are needed for computing `V`.
1717+In `Fx<S, V>`, `S` is the *Ability* (sometimes called the set of Abilities, Capabilities, or Effect Environment) needed to compute `V`.
21182222-Abilities describe the external resources that would be needed, as well as the _side-effects_ that are possible while computing `V`.
1919+Abilities describe the external resources or side-effects possible while computing `V`.
23202424-Examples of such Abilities are:
2121+Examples:
25222626-- network abilities (eg, performing http requests)
2727-- console abilities (eg, printing to the terminal or reading user input)
2828-- non-deterministic abilities (eg, generating random numbers or coin-flips)
2929-- resource handling (eg, disciplined acquire/use/release of shared/limited resources)
3030-- exception handling (eg, interruption/resumption and finalizers)
3131-- anything else that interacts with the world outside of the program.
2323+- network abilities (e.g., HTTP requests)
2424+- console abilities (e.g., printing, reading input)
2525+- non-deterministic abilities (e.g., random numbers)
2626+- resource handling (e.g., managing shared resources)
2727+- exception handling (e.g., interruption, finalizers)
2828+- anything else that interacts with the outside world
32293330## Handlers
34313535-A *Handler* for the `S` ability is a particular _interpretation_ of what `S` means.
3232+A *Handler* for an ability is a particular _interpretation_ of what that ability means.
36333737-Handlers are the only _side-effectful_ portion of your programs. It is possible, and quite common, to have different handlers (interpretations) for the same Ability, and each Handler decides _how/when_ to perform world-modifying _side-effects_.
3434+Handlers are the only side-effectful part of your programs. You can have different handlers for the same ability, and each handler decides how and when to perform world-modifying side-effects.
38353939-For example, for an _http-request_ ability you can have a *test-handler* that just mock responses to fixed values so that you can easily assert on known values on your tests. You could also have a *live-handler* that actually performs requests via the network for production runs.
3636+For example, for an HTTP ability, you can have a test handler that mocks responses for tests, or a live handler that performs real network requests in production.
+21
book/src/concurrency.md
···11+# Structured Concurrency in fx-rs
22+33+fx-rs models concurrency primitives (spawning, channels, async tasks) as abilities. This makes concurrent operations explicit, composable, and testable.
44+55+## Composable Concurrency
66+77+You can add concurrency abilities to your context, and write effectful code that requests concurrency without knowing the underlying executor. Handlers can swap between real and mock concurrency backends for robust testing.
88+99+## Example
1010+1111+```rust
1212+trait Spawn {
1313+ fn spawn<Fut: Future<Output = ()> + Send + 'static>(&self, fut: Fut);
1414+}
1515+1616+// Add Spawn to your context and use it in effectful code.
1717+```
1818+1919+______________________________________________________________________
2020+2121+This approach enables structured concurrency, deterministic testing, and modular integration with Rust's async ecosystem.
-3
book/src/contributing.md
···11-# Contributing
22-33-Yes, please. All PRs are welcome, documentation, code improvements, ideas or suggestions, ways to improve this book prose, etc.
+18-5
book/src/core.md
···11# Core Effects
2233-The list of effects provided by `Fx.go` will increase as we see the need for them and as long as they provide useful on Golang programs.
33+The list of effects provided by `fx-rs` will grow as new needs are discovered in Rust programs.
44+55+Current ideas for contributions:
66+77+- Resource management (safe acquire/use/release of resources)
88+- Structured concurrency (beyond manual mutex/channels)
99+- Any other effectful pattern useful in Rust—issues and PRs are welcome!
1010+1111+```rust
1212+// Type alias for a function effect
1313+type StringToUsize = Fx<String, usize>;
41455-Current Ideas for contributions:
1515+fn length_of_string(s: String) -> usize { s.len() }
61677-- Resource management (reserve/use/release shared resources)
88-- Structured concurrency (instead of manually using mutex/channels)
99-- any other idea you find useful, [Issues and PRs](https://github.com/vic/fx.go) are welcome!
1717+let effect: StringToUsize = Fx::func(length_of_string);
1818+let requirement = "Hello World".to_owned();
1919+let provided = effect.provide(requirement.clone());
2020+let result: usize = provided.eval();
2121+assert_eq!(result, requirement.len()); // result: usize
2222+```
+31
book/src/di_typeclasses.md
···11+# Dependency Injection and Programmable Typeclasses
22+33+fx-rs unifies algebraic effects and dependency injection. Handlers are first-class values, not static typeclasses, so you can swap them at runtime and scope them to subcomputations.
44+55+## Example: Swapping Handlers for a Subcomputation
66+77+```rust
88+use fx::Fx;
99+1010+trait Logger { fn log(&self, msg: &str); }
1111+struct ProdLogger;
1212+impl Logger for ProdLogger { fn log(&self, msg: &str) { println!("prod: {}", msg); } }
1313+struct TestLogger;
1414+impl Logger for TestLogger { fn log(&self, msg: &str) { println!("test: {}", msg); } }
1515+1616+struct AppContext<L: Logger> { logger: L }
1717+1818+fn log_something<'f, L: Logger>(msg: &'f str) -> Fx<'f, L, ()> {
1919+ Fx::pure(move |l: &L| l.log(msg))
2020+}
2121+2222+let prod_ctx = AppContext { logger: ProdLogger };
2323+let fx = log_something("main");
2424+let _ = fx.run(&prod_ctx.logger);
2525+2626+// Swap to a test logger for a subcomputation
2727+let test_ctx = AppContext { logger: TestLogger };
2828+let _ = fx.adapt(|_| &test_ctx.logger, |c, r| (c, r)).run(&prod_ctx.logger);
2929+```
3030+3131+This enables modular, testable, and flexible dependency management—without global singletons or static typeclasses.
+33
book/src/direct_style.md
···11+# Direct-Style and Builder APIs
22+33+fx-rs supports not just monadic chaining but also direct-style macros and builder patterns for ergonomic effectful code.
44+55+## Example: Direct-Style Macro with fx_do!
66+77+The `fx_do!` macro allows you to write effectful code in a direct, imperative style. Under the hood, `.same()` is used for `map_m` (monadic map), and `.bind()` is used for `flat_map` (monadic bind):
88+99+```rust
1010+use fx::Fx;
1111+use fx_do::fx_do;
1212+1313+fx_do! {
1414+ let x = Fx::pure(1);
1515+ let y = x.same(); // equivalent to .map_m
1616+ let z = y.bind(); // equivalent to .flat_map
1717+ Fx::pure(z)
1818+}
1919+```
2020+2121+## Example: Builder Pattern
2222+2323+```rust
2424+use fx::Fx;
2525+2626+let result = Fx::builder()
2727+ .get_value()
2828+ .compute()
2929+ .log()
3030+ .run();
3131+```
3232+3333+These patterns reduce boilerplate and make effectful code look and feel like regular Rust.
+21
book/src/environments.md
···11+# First-Class Environments and Context Structs
22+33+Contexts in fx-rs are just Rust structs, supporting named, nested, and multiple abilities.
44+55+## Example: Composing Contexts
66+77+```rust
88+use fx::Fx;
99+1010+struct Logger;
1111+struct HttpClient;
1212+struct AppContext { logger: Logger, http: HttpClient }
1313+1414+fn log_and_fetch<'f>(ctx: &'f AppContext) -> Fx<'f, AppContext, ()> {
1515+ Fx::pure(move |c: &AppContext| {
1616+ // Use c.logger and c.http
1717+ })
1818+}
1919+```
2020+2121+This makes dependency management explicit, readable, and maintainable.
+34
book/src/future_work.md
···11+# Future Work & Research Directions
22+33+fx-rs is designed to be extensible and to inspire further research and development in effect systems for Rust. Some promising directions and open questions include:
44+55+- **Modular Reasoning & Scoped Handlers:**
66+77+ - Advanced patterns for modular, component-based architectures using fx-rs, where each module declares its effect signature and dependencies as traits.
88+ - Scoped handler replacement (`Fx::adapt`) for targeted testing, feature flags, and environment-specific behavior.
99+ - Building up application context from smaller module contexts for scalable composition.
1010+1111+- **Effect Row Inference and Minimization:**
1212+1313+ - Static and dynamic effect row inference for scalable effect systems.
1414+ - Linter and IDE integration for effect signature inference and minimization.
1515+ - Exploring trade-offs between manual and inferred effect signatures.
1616+1717+- **Security and Capability Effects:**
1818+1919+ - Modeling permissions and capabilities as effects/abilities.
2020+ - Compile-time enforcement of security policies via capabilities.
2121+ - Patterns for capability injection, fine-grained permission checks, and compile-time security.
2222+2323+- **First-Class, Type-Safe Effect Scopes:**
2424+2525+ - Local reasoning and effect masking (scoping effects to regions).
2626+ - Encoding effect scopes in Rust's type system.
2727+ - Lexical vs. dynamic scoping for effects.
2828+2929+- **Resumable and Multi-Shot Handlers:**
3030+3131+ - Support for resumable exceptions, continuations, and multi-shot handlers.
3232+ - Encoding resumable/multi-shot handlers in Rust and exploring their use cases.
3333+3434+If you are interested in contributing to any of these areas, or have ideas for new features, please open an issue or discussion on the fx-rs repository!
···11# Handlers
2233-A _Handler_ is an effect transformation function of type `func(Fx[R, U]) Fx[S, V]`.
33+A _Handler_ is an effect transformation function of type `impl Handler<'f, R, S, U, V>` (see the `Handler` trait in fx-rs).
4455-Handlers are free to change the effect requirements, they tipically reduce the requirement set, but they could also introduce new requirements. They can also change or keep the result type.
55+Handlers can change effect requirements, typically reducing them, but may also introduce new requirements or change the result type.
6677-## Handling an effect
77+## Handling an Effect
8899-Lets re-write our previous "length of string" function as a Handler.
99+Let's rewrite the "length of string" function as a handler in Rust:
10101111-```go
1212-type LenFn func(string) Fx[Nil, int]
1111+```rust
1212+use fx::Fx;
1313+use fx::Handler;
13141414-// Code is type annotated for clarity
1515-var lenFx Fx[And[LenFn, Nil], int] = fx.Suspend[LenFn]("hello")
1616-1717-// type of a handler. not needed but added for clarity.
1818-type LenHn func(Fx[And[LenFn, Nil], int]) Fx[Nil, int]
1515+// Type alias for handler effect
1616+type RWHandler = Fx<Env, Output>;
19172020-var handler LenHn = fx.Handler(func(s string) fx.Fx[fx.Nil, int] {
2121- return fx.Pure(len(s))
2222-})
1818+// Handler that takes Fx<(LenFn, ()), usize> and returns Fx<(), usize>
1919+let len_handler: RWHandler = |fx: Fx<'static, (fn(String) -> Fx<'static, (), usize>, ()), usize>| {
2020+ fx.provide_left(|s: String| Fx::pure(s.len()))
2121+};
23222424-// apply the handler directly to lenFx
2525-var x *int = fx.Eval(handler(lenFx))
2626-assert(*x == 5)
2323+let effect: Fx<'static, (fn(String) -> Fx<'static, (), usize>, ()), usize> = Fx::func(|s: String| Fx::pure(s.len()));
2424+let handled: Fx<'static, (), usize> = len_handler.handle(effect);
2525+let result = handled.eval();
2626+assert_eq!(result, "hello".len());
2727```
28282929-As you might guess, `fx.Handler` is just a wrapper for `ProvideLeft(Fx[And[Fn, S], O], Fn) Fx[S, O]` where `Fn = func(I) Fx[S, O]`, an request-effect function.
2929+## Requesting Handlers from the Environment
30303131-## Requesting Handlers (effect-transformers) from the environment.
3131+You can also request that a handler be present as a requirement. This way, the handler is provided once and can be applied anywhere in the program.
32323333-Of course, you can also request that a particular effect transformer (Handler) be present as a requirement of some computation. This way the handler is provided only once but can be applied anywhere it is needed inside the program.
3333+```rust
3434+use fx::Fx;
3535+use fx::Handler;
34363535-```go
3636-// Same examples as above with some more types for clarity
3737+// Type alias for handler effect
3838+type RWHandler = Fx<Env, Output>;
37393838-// effect-request function type.
3939-type LenFn func(string) Fx[Nil, int]
4040-// effect handler type
4141-type LenHn = func(Fx[And[LenFn, Nil], int]) Fx[Nil, int]
4040+let len_handler: RWHandler = |fx: Fx<'static, (fn(String) -> Fx<'static, (), usize>, ()), usize>| {
4141+ fx.provide_left(|s: String| Fx::pure(s.len()))
4242+};
42434343-// effect ability
4444-type LenAb = And[LenHn, Nil]
4545-// effect type producing V
4646-type LenFx[V any] = fx.Fx[LenAb, V]
4444+let effect: Fx<'static, (fn(String) -> Fx<'static, (), usize>, ()), usize> = Fx::func(|s: String| Fx::pure(s.len()));
4545+let provided = effect.provide_left(len_handler);
4646+let result = provided.eval();
4747+assert_eq!(result, "hello".len());
4848+```
47494848-// Same as: Suspend[LenHn](Suspend[LenFn](input))
4949-var lenFx LenFx = fx.Handle[LenHn]("hello")
5050+Handlers in fx-rs are just values and can be passed, composed, or swapped as needed.
50515151-var handler LenHn = fx.Handler(func(s string) fx.Fx[fx.Nil, int] {
5252- return fx.Pure(len(s))
5353-})
5252+A **Handler** in fx-rs is a transformation: it takes an input (often an effectful request or ability) and produces a new effect. Conceptually, a handler is a function that interprets or transforms effects, often by providing implementations for abilities or by composing/rewriting effects. See the comment in `handler.rs` for details.
54535555-// Now instead of applying the handler directly to each effect
5656-// we provide it into the environment.
5757-var provided Fx[Nil, int] = fx.ProvideLeft(lenFx, handler)
5858-val x int = fx.Eval(provided)
5959-assert(x == 5)
6060-```
5454+An **Ability** is a trait or type that represents a capability or effectful operation. In fx-rs, an ability is conceptually a function of the form `I => Fx<S, O>`, meaning it takes an input `I` and returns an effectful computation producing an output `O` and possibly requiring further abilities `S`. See the comment in `ability.rs` for the canonical definition.
-62
book/src/http.md
···11-# Example HTTP Req/Res program
22-33-As an example of the concepts we have seen so far, lets write a program that needs to perform an HTTP Request, expects an HTTP response from the webservice it accesses and then perform some logic on that response.
44-55-On an effect system like `Fx.go`, we *do not* directly contact external services, we just express our need to perform such requests and we expect a `Handler` to actually decide how and when such request should be performed (if any).
66-77-```go
88-package http_example
99-1010-// Notice we do not import any HTTP library, just effects.
1111-import (
1212- "testing"
1313- fx "github.com/vic/fx.go"
1414-)
1515-1616-// For simplicity our request is just an string: An URL.
1717-type HttpRq string
1818-1919-// For simplicity out response is just an string: The response body.
2020-type HttpRs string
2121-2222-// Type of the effect-request function.
2323-// This will be implemented by some handler to provide actual responses.
2424-//
2525-// fx.Nil in the result type indicates that our Http ability does
2626-// not requires any other ability.
2727-type HttpFn func(HttpRq) fx.Fx[fx.Nil, HttpRs]
2828-2929-// Type of the Http Handler that discharges effect requirements
3030-type HttpHn = func(fx.Fx[fx.And[HttpFn, fx.Nil], HttpRs]) fx.Fx[fx.Nil, HttpRs]
3131-3232-// Type of the Http Ability: the handler and aditional abilities.
3333-type HttpAb = fx.And[HttpHn, fx.Nil]
3434-3535-// An http effect that produces V
3636-type HttpFx[V any] = fx.Fx[HttpAb, V]
3737-3838-// An effect of HTTP GET requests.
3939-func Get(url HttpRq) HttpFx[HttpRs] {
4040- return fx.Handle[HttpHn](url)
4141-}
4242-4343-// A program that computes the respose length of https://example.org
4444-func Program() HttpFx[int] {
4545- return fx.Map(Get("https://example.org"), func(r HttpRs) int {
4646- return len(r)
4747- })
4848-}
4949-5050-func TestProgram(t *testing.T) {
5151- var httpHandler HttpHn = fx.Handler(func(r HttpRq) fx.Fx[fx.Nil, HttpRs] {
5252- mock := HttpRs("example")
5353- return fx.Pure(&mock)
5454- })
5555- var provided fx.Fx[fx.Nil, int] = fx.ProvideLeft(Program(), httpHandler)
5656- var result int = fx.Eval(provided)
5757- if result != len("example") {
5858- t.Errorf("Unexpected result %v", result)
5959- }
6060-}
6161-6262-```
+62
book/src/macros.md
···11+# Macro-Based Ergonomics in fx-rs
22+33+fx-rs uses Rust's macro system to provide ergonomic APIs and reduce boilerplate for effectful programming. Some highlights:
44+55+## Ability Derivation
66+77+Procedural macros in the `abilities_macro` crate allow you to derive ability traits and handlers automatically, making it easy to define new effectful operations and their interpreters.
88+99+## Direct-Style Macros
1010+1111+The `do_macro` and `do_traits` crates provide macros for writing effectful code in a direct, imperative style, reducing the need for manual chaining and improving readability.
1212+1313+### Example: fx_do! with .same() and .bind()
1414+1515+The `fx_do!` macro allows you to write effectful code in a direct style. Under the hood, `.same()` is used for `map_m` (monadic map), and `.bind()` is used for `flat_map` (monadic bind):
1616+1717+```rust
1818+use fx::Fx;
1919+use fx_do::fx_do;
2020+2121+// Type alias for macro-based effect
2222+type MacroEff = Fx<MacroEnv, MacroVal>;
2323+2424+fx_do! {
2525+ let x: MacroEff = Fx::pure(1);
2626+ let y = x.same(); // equivalent to .map_m
2727+ let z = y.bind(); // equivalent to .flat_map
2828+ Fx::pure(z)
2929+}
3030+```
3131+3232+## Lens and Field Macros
3333+3434+The `lens_macro`, `field_macro`, and `forall_macro` crates provide macros for generating lenses and working with generic fields, enabling fine-grained, type-safe state manipulation and modular effect composition.
3535+3636+### field_macro: Field-Based Lenses
3737+3838+The `field_macro` crate provides macros to automatically generate lenses for struct fields, making it easy to focus on and update nested fields in your state.
3939+4040+#### Example: field_macro
4141+4242+```rust
4343+use field_macro::Field;
4444+4545+#[derive(Field, Clone)]
4646+struct AppState {
4747+ #[field]
4848+ counter: u32,
4949+ #[field]
5050+ user: User,
5151+}
5252+5353+// Now you can use AppState::counter() and AppState::user() as lenses.
5454+```
5555+5656+### forall_macro: Quantified Field Access
5757+5858+The `forall_macro` crate provides macros for working with generic fields and quantified access, enabling advanced patterns for generic and reusable effectful code.
5959+6060+______________________________________________________________________
6161+6262+These macros are designed to work seamlessly with the fx-rs core, making advanced effect patterns accessible and ergonomic for Rust developers.
+20
book/src/performance.md
···11+# Performance and Trade-offs
22+33+fx-rs is designed for flexibility and composability, but also aims for competitive performance. It uses dynamic dispatch for handler values, but leverages Rust's inlining and monomorphization where possible.
44+55+## Example: Static vs Dynamic Dispatch
66+77+```rust
88+use fx::Fx;
99+1010+fn static_add(n: u32) -> u32 { n + 1 }
1111+let fx_static = Fx::func(static_add);
1212+let result = fx_static.provide(41).eval();
1313+assert_eq!(result, 42);
1414+1515+let fx_dyn = Fx::pending(|n: u32| Fx::value(n + 1));
1616+let result = fx_dyn.provide(41).eval();
1717+assert_eq!(result, 42);
1818+```
1919+2020+Use static dispatch for performance-critical code, and dynamic handler values for flexibility and modularity.
+19
book/src/polymorphism.md
···11+# Effect Polymorphism and Generic Abstractions
22+33+fx-rs enables writing code generic over effects, supporting reusable libraries and higher-order effectful functions.
44+55+## Example: Generic Logging
66+77+```rust
88+use fx::Fx;
99+1010+trait Log { fn log(&self, msg: &str); }
1111+1212+fn with_logging<'f, L: Log>(msg: &'f str) -> Fx<'f, L, ()> {
1313+ Fx::pure(move |l: &L| l.log(msg))
1414+}
1515+1616+// Can be used with any Log implementation
1717+```
1818+1919+This enables scalable, composable effectful code and reusable libraries.
+24
book/src/req_apply.md
···11+# Functional Requirements
22+33+As seen in the previous [Context](context.html) chapter, you can require any type to be part of an effect's environment.
44+55+You can also require functions of some type to be present and use them in your program descriptions without knowing their exact implementation.
66+77+For example, suppose we require a function from `usize` to `usize` and then apply it to a value:
88+99+```rust
1010+// Type alias for a function requirement
1111+type FnReq = Fx<fn(usize) -> usize, usize>;
1212+1313+fn double(n: usize) -> usize { n * 2 }
1414+1515+// The effect requires a function of type fn(usize) -> usize
1616+let effect: FnReq = Fx::require();
1717+// Provide the function implementation as the requirement
1818+let applied = effect.provide(double);
1919+// Provide the input value to the function
2020+let result = applied.provide(12).eval();
2121+assert_eq!(result, 24); // result: usize
2222+```
2323+2424+For more complex cases, you can nest effect requests and flatten them using combinators, as in the fx-rs API.
+29
book/src/req_context.md
···11+# Context Requirements
22+33+The `State::get()` function creates an effect that requires some value `S` as part of the environment and evaluates to `S`.
44+55+It can be used to request the presence of services (traits or collections of methods that produce related effects) or, more generally, evidence that a value is part of the environment.
66+77+For example, mapping over a string to compute its length:
88+99+```rust
1010+use fx::State;
1111+1212+// Type alias for requirement effect
1313+type StringLen = Fx<String, usize>;
1414+1515+let eff: StringLen = State::get::<String>().map(|s| s.len());
1616+let result: usize = eff.provide("hello".to_owned()).eval();
1717+assert_eq!(result, 5); // result: usize
1818+```
1919+2020+Calling `Fx::value` is like mapping a context over a constant function. `Fx::pure` is defined in terms of `Fx::value`.
2121+2222+```rust
2323+use fx::State;
2424+use fx::Fx;
2525+2626+let a = State::get::<String>().map(|_| 42);
2727+let b = Fx::value(42);
2828+let c = Fx::pure(22);
2929+```
+27
book/src/req_providers.md
···11+# Providing Requirements
22+33+Effect requirements can be provided in any order, with no impact on program evaluation.
44+55+## Provide Combinators
66+77+These functions are used to eliminate requirements from effects. Only when `()` is the only remaining requirement can the effect be evaluated.
88+99+```rust
1010+use fx::Fx;
1111+1212+// Type alias for a requirement-providing effect
1313+type TimesTen = Fx<usize, usize>;
1414+1515+let fx: TimesTen = Fx::pending(|n: usize| Fx::value(n * 10));
1616+let fx2 = fx.provide(12);
1717+assert_eq!(fx2.eval(), 120); // result: usize
1818+1919+// Type alias for a pair requirement
2020+type PairReq = Fx<(i32, i32), i32>;
2121+2222+let fx: PairReq = Fx::value(7);
2323+let fx2 = fx.provide_left::<i32, i32>(1);
2424+// ...
2525+```
2626+2727+See the fx-rs API for more combinators and details.
+41
book/src/req_shuffling.md
···11+# Shuffling `And`ed Requirements
22+33+In `fx-rs`, requirements are identified by their type, not by name or position. Effect requirements can be freely rearranged with no impact on program meaning.
44+55+## `And` Combinators
66+77+Several combinators help you rearrange and manipulate `And`ed effect requirements:
88+99+```rust
1010+use fx::Fx;
1111+1212+// Type alias for collapsed requirements
1313+type Collapsed = Fx<u32, u32>;
1414+1515+let fx: Fx<(u32, u32), u32> = Fx::immediate((10u32, 10u32), 20u32);
1616+let fx2: Collapsed = fx.and_collapse::<u32>();
1717+let result: u32 = fx2.provide(10).eval();
1818+assert_eq!(result, 20); // result: u32
1919+2020+// Add a Nil (unit) requirement
2121+let fx = Fx::immediate(21u32, 42u32);
2222+let fx2 = fx.and_nil::<(u32, ())>();
2323+let result = fx2.provide((21, ())).eval();
2424+assert_eq!(result, 42);
2525+2626+// Swap requirements
2727+let fx = Fx::immediate((5u8, 10u16), 15u16);
2828+let fx2 = fx.and_swap::<u8, u16, (u16, u8)>();
2929+let result = fx2.provide((10u16, 5u8)).eval();
3030+assert_eq!(result, 15);
3131+3232+// Nest and flatten requirements
3333+let fx = Fx::immediate((3u8, 7u16), 21u16);
3434+let nested = fx.and_nest::<u8, u16>();
3535+let inner = nested.provide(3u8).eval();
3636+let result = inner.provide(7u16).eval();
3737+assert_eq!(result, 21);
3838+let flat = fx.and_nest::<u8, u16>().and_flat::<(u8, u16)>();
3939+let result2 = flat.provide((4u8, 6u16)).eval();
4040+assert_eq!(result2, 21);
4141+```
-37
book/src/requests.md
···11-# Effect Requests
22-33-Another way of creating effects in `Fx.go` is via an *effect-request* function.
44-55-A function of type `func(I) Fx[S, O]` is said to take an _effect-request_ `I` and produce an *suspended* effect `Fx[S, O]`.
66-77-An example, is the function `func(HttpReq) Fx[HttpService, HttpRes]` we saw in the [previous](./requirements/apply.html) chapter.
88-99-Using the "length of string" example of the previous chapters, we can use it to model an effect request:
1010-1111-```go
1212-type LenFn = func(string) fx.Fx[fx.Nil, int]
1313-1414-// Code is type annotated for clarity
1515-var lenFx fx.Fx[fx.And[LenFn, fx.Nil], int] = fx.Suspend[LenFn]("hello")
1616-```
1717-1818-Note that `Suspend` takes the _type_ of a request-effect function and the request value for it. And yields a *suspended* effect of type `Fx[And[LenFn, Nil], int]`. The computation is said to be *suspended* because it knows not what particular implementation of `LenFn` should be used, and because of this, `LenFn` is part of the requirements, along with `Nil` the ability requirement on the result of `LenFn`.
1919-2020-Different implementations of `LenFn` can be provided to the `lenFx` effect.
2121-2222-```go
2323-var lies LenFn = func(_ string) fx.Fx[fx.Nil, int] {
2424- return fx.Pure(42)
2525-}
2626-var truth LenFn = func(s string) fx.Fx[fx.Nil, int] {
2727- return fx.Pure(len(s))
2828-}
2929-3030-var x int = fx.Eval(fx.ProvideLeft(lenFx, lies))
3131-assert(x == 42)
3232-3333-var y int = fx.Eval(fx.ProvideLeft(lenFx, truth))
3434-assert(y == 5)
3535-```
3636-3737-Notice that by delaying which implementation of `LenFn` is used, the `lenFx` program description includes the effect request `"hello"` and knows the general form of its response `Fx[Nil, int]`, but knows nothing about which particular interpretation of `LenFn` will be used.
+2-59
book/src/requirements.md
···11# Effect Requirements
2233-So far, we have seen that an effect `Fx[S, V]` can have `S` be `Nil` for effects that can be evaluated right away and non-`Nil` for those pending effects that still need to be provided some value.
44-55-In this chapter we will talk about aggregating requirements using the `And` type. And how different types of functions/effects can represent the very same computation. We also look at similarities with higher-order functions in functional-programming and how rotating or re-arranging effect requirements is indifferent in `Fx.go`. Finally, we show some the`And*` and `Provide*` combinators that can help you reshape your effect requirements.
66-77-## Composite requirements
88-99-Using the same "length of string" function from the previous chapters, we can describe it in different ways.
33+In `fx-rs`, effect requirements are the types that must be present in the environment for an effect to be evaluated. Requirements can be provided in any order, and combinators exist to manipulate and discharge them.
1041111-```go
1212-// This is an strange way of writing `func(string) int`.
1313-// But this shape can help to understand the types bellow.
1414-//
1515-// Focus your attention on the requirements that are
1616-// needed for a computing a value.
1717-//
1818-// In particular, note that `Fx[Nil, V]` is like a `func() V`
1919-func LengthOfString(s string) func() int {
2020- return func() int { return len(s) }
2121-}
2222-2323-// The type of LengthOfString expressed as an effect request.
2424-type LenFn = func(string) Fx[Nil, int]
2525-```
2626-2727-Note that all of the following types are equivalent, as they describe the very same requirements and result types:
2828-2929-- `func(string) int`
3030-- `Fx[string, int]`
3131-- `func(string) func() int`
3232-- `func(string) Fx[Nil, int]`
3333-- `Fx[string, Fx[Nil, int]]`
3434-- `Fx[And[string, Nil], int]`
3535-- `Fx[And[Nil, string], int]`
3636-3737-The last three examples represent nested effects and are equivalent to functions of arity > 1 or functions that return functions.
3838-3939-`And[A, B]` is the requirement for both `A` and `B` abilities. Notice in the last two examples, that their components are swapped. It is important to note that in `Fx.go`, _the *order* of the abilities on And requirements does not matter_ and they can be freely swapped/joined/unjoined. More on this when we talk about `And*` combinators.
4040-4141-Also, note that `And[A, Nil]` is equivalent to just `A`. All of these types represent the same type of computation and an effect can be transformed to any of those types freely.
4242-4343-## `>1` arity functions as effects.
4444-4545-Suppose you have a function that multiplies an string length by n.
4646-4747-```go
4848-func MulLen(s string, n int) int {
4949- return len(s) * n
5050-}
5151-```
5252-5353-`MulLen` can be described by the following types:
5454-5555-- `func(string, int) int`
5656-- `func(string) func(int) int`
5757-- `Fx[And[string, int], int]`
5858-- `Fx[string, Fx[int, int]]`
5959-- `Fx[int, Fx[string, int]]`
6060-- `Fx[And[int, string], int]`
6161-6262-Note that `And[X, X]` is equivalent to just a single `X` requirement, and that `And[And[X, Y], And[Y, X]]` is also equivalent to `And[X, Y]`.
55+See the subchapters for details on applying, shuffling, and providing requirements in Rust.
-48
book/src/requirements/apply.md
···11-# Functional Requirements
22-33-As we have seen in the previous [Context](context.html) chapter, you can ask for any type to be part of an effect's environment.
44-55-You can also ask for functions of some type to be present and use them in your program descriptions without knowing their exact implementation.
66-77-For example, suppose we ask for a function that takes an integer. And then apply it to a value.
88-99-```go
1010-// The type of the function from int to int
1111-type OnInt func(int) nit
1212-1313-// Apply OnInt(12)
1414-var a Fx[OnInt, int] = Map(Ctx[OnInt](), func(f OnInt) int {
1515- return f(12)
1616-})
1717-1818-// fx.Apply is just an abbreviation of the previous code:
1919-// It takes the type of a unary function and the value to apply.
2020-var b Fx[OnInt, int] = Apply[OnInt](12)
2121-```
2222-2323-Because `OnInt` is part of the environment, we only know its signature (its request and response type), but not the actual implementation of it. This way, different implementations of `OnInt` can be provided later.
2424-2525-Now, suppose you have the following code:
2626-2727-```go
2828-type HttpRq string
2929-var url = HttpRq("https://example.org")
3030-3131-// `Http` is the type of an *effect request* function.
3232-// it takes a plain value and creates an effect.
3333-type Http func(HttpRq) Fx[HttpService, HttpRs]
3434-3535-// NOTE: applying to Http, creates a nested effect
3636-var a Fx[Http, Fx[HttpService, HttpRs]] = Apply[Http](url)
3737-3838-// Flatenning creates an Anded environment.
3939-var b Fx[And[Http, HttpService], HttpRs] = AndJoin(a)
4040-4141-4242-// Same as previous two lines:
4343-var x Fx[And[Http, HttpService], HttpRs] = Suspend[Http](url)
4444-```
4545-4646-`fx.Suspend` is an abbreviation of `AndJoin(Apply[Http](url))` for *effect requests* (functions returning an effect)
4747-4848-This is the principle behind *suspended effect application* in `Fx.go` and is a fundamental part when we talk about effect [Requests](../requests.html) and [Handlers](../handlers.html).
-24
book/src/requirements/context.md
···11-# Context Requirements
22-33-The `Ctx[V]() Fx[V, V]` function creates an effect that requires some value `V` as part of the environment and evaluates to `V`.
44-55-It can be used to request the presence of some services (interfaces or collections of methods that produce related effects) or more generally, an evidence that a value `V` is part of the environment.
66-77-In the following example we simply map over an string to compute its length.
88-99-```go
1010-var eff Fx[string, int] = Map(Ctx[string](), LengthOfString)
1111-var result int = Eval(Provide(eff, "hello"))
1212-assert(result == len("hello"))
1313-```
1414-1515-Calling the `Const[S](V) Fx[S, V]` is like maping a context over a constant function.
1616-As you might have guessed, `Pure` is defined in terms of `Const`.
1717-1818-```go
1919-var a Fx[string, int] = Map(Ctx[string](), func(_ string) int { return 42 })
2020-var b Fx[string, int] = Const[string](42)
2121-2222-var c Fx[Nil, int] = Pure(22)
2323-var d Fx[Nil, int] = Const[Nil](22)
2424-```
-35
book/src/requirements/providers.md
···11-# Providing Requirements
22-33-*Effect requirements can be provided in any order* with no impact in the evaluation of the program.
44-55-## `Provide*` Combinators.
66-77-These functions are used to eliminate requirements from effects. Only when `Nil` is the only remaining requirement, the effect can be evaluated.
88-99-The result of provide functions will always be another effect, meaning that no computation has been performed yet, nor any side-effect produced. The result is just another effect but with less requirements.
1010-1111-```go
1212-// Discharges the single S requirement.
1313-func Provide(Fx[S, V], S) Fx[Nil, V]
1414-1515-1616-// Discharges the requirement of A by providing it.
1717-func ProvideLeft(Fx[And[A, B], V], A) Fx[B, V]
1818-1919-// Discharges the requirement of B by providing it.
2020-func ProvideRight(Fx[And[A, B], V], B) Fx[A, V]
2121-2222-// Discharges both A and B
2323-func ProvideBoth(Fx[And[A, B], V], A, B) Fx[Nil, V]
2424-2525-2626-2727-// Provides A, the first part of the left And.
2828-func ProvideFirstLeft(Fx[And[And[A, C], And[B, D]], V], A) Fx[And[C, And[B, D]], V]
2929-3030-// Provides B, the first part of the right And.
3131-func ProvideFirstRight(Fx[And[And[A, C], And[B, D]], V], B) Fx[And[And[A, C], D], V]
3232-3333-// Provides A and B, the first part of both Ands.
3434-func ProvideFirsts(Fx[And[And[A, C], And[B, D]], V], A, B) Fx[And[C, D], V]
3535-```
-33
book/src/requirements/shuffling.md
···11-# Shuffling `And`ed requirements.
22-33-An important thing to note is that in `Fx.go`, *requirements are identified by their type* and not by name or position as in regular functions.
44-55-*Effect requirements can be freely re-arranged* with no impact in the meaning of the program.
66-77-## `And*` combinators.
88-99-There are some functions (more will be added as they are found useful) that help you re-arrange `And`ed effect requirements.
1010-1111-Shuffling And requirements is useful because you might want to provide some requirements first than others even if they are not in the head position of the requirement list.
1212-1313-```go
1414-// Since `And[A, A]` is equivalent to just `A`.
1515-// Used to collapse Nil requirements just before evaluation.
1616-func AndCollapse(Fx[And[A, A], V]) Fx[A, V]
1717-1818-// Ands S with Nil in the effect requirements
1919-func AndNil(Fx[S, V]) Fx[And[S, Nil], V]
2020-2121-// Swaps A and B. Note: this has no impact on how computation is
2222-// performed, since requirements can be freely re-arranged.
2323-func AndSwap(Fx[And[A, B], V]) Fx[And[B, A], V]
2424-2525-2626-// FlatMaps the inner effect into the outer by
2727-// Anding their requirements.
2828-func AndJoin(Fx[A, Fx[B, V]]) Fx[And[A, B], V]
2929-3030-// Reverse of Join
3131-func AndDisjoin(Fx[And[A, B], V]) Fx[A, Fx[B, V]]
3232-3333-```
+19
book/src/resources.md
···11+# Resource Management and Bracket Patterns
22+33+fx-rs supports safe, composable resource management using bracket-like APIs and Rust’s ownership model.
44+55+## Example: File Handling
66+77+```rust
88+use fx::Fx;
99+1010+fn with_file<'f>(path: &'f str) -> Fx<'f, (), String> {
1111+ Fx::pure(move |_| format!("opened {}", path))
1212+}
1313+1414+let fx = with_file("foo.txt");
1515+let result = fx.eval();
1616+assert_eq!(result, "opened foo.txt");
1717+```
1818+1919+Resources are acquired, used, and released safely, with automatic cleanup and error handling.
+2-9
book/src/rw.md
···11# Reader/Writer
2233-A `Reader[T]` allows to read values of type `T` from the environment while `Writer[T]` allows to set them.
44-55-- Implementation
33+A `Reader<T>` allows reading values of type `T` from the environment, while `Writer<T>` allows setting them.
6477- - [read.go](https://github.com/vic/fx.go/blob/main/rw/read.go)
88- - [write.go](https://github.com/vic/fx.go/blob/main/rw/write.go)
99-1010-- Tests [rw_test.go](https://github.com/vic/fx.go/blob/main/rw/rw_test.go)
1111-1212-Read and Write Handlers take an effectful operation that can modify the external world. See `TestReadWrite`.
55+Read and Write handlers take an effectful operation that can modify the external world. See the fx-rs codebase and tests for usage examples.
+102
book/src/state_lenses.md
···11+# State Effects, Lenses, and Contexts in fx-rs
22+33+fx-rs provides modular and composable state effects, with first-class support for lenses, struct-based contexts, and advanced combinators for working with state and context.
44+55+## Modular State and Lenses
66+77+State effects are not global; you can focus on any part of your context using lenses. This allows you to write effectful code that only depends on the state it needs, improving modularity and testability.
88+99+### Example: Using a Lens
1010+1111+```rust
1212+use fx::{State, Lens};
1313+1414+// Type alias for state lens effect
1515+type LensEff = Fx<State, Value>;
1616+1717+#[derive(Clone)]
1818+struct AppState {
1919+ counter: u32,
2020+ user: User,
2121+}
2222+2323+#[derive(Clone)]
2424+struct User {
2525+ name: String,
2626+}
2727+2828+let lens = Lens::<AppState, u32>::new();
2929+let get_counter = State::<AppState>::get().via(lens.zoom_out());
3030+```
3131+3232+## Struct-Based Contexts and Pair
3333+3434+fx-rs supports both tuple and struct-based contexts. The `Pair` trait allows you to use custom structs as effect environments, not just tuples.
3535+3636+### Example: Pair for Struct Context
3737+3838+```rust
3939+use fx::Pair;
4040+4141+#[derive(Clone)]
4242+struct Ctx { a: i32, b: bool }
4343+4444+impl Pair<i32, bool> for Ctx {
4545+ fn fst(self) -> i32 { self.a }
4646+ fn snd(self) -> bool { self.b }
4747+}
4848+```
4949+5050+This enables ergonomic, named, and type-safe contexts for your effects.
5151+5252+## forall: Quantified Effects
5353+5454+The `forall` combinator allows you to abstract over part of the context, making your effect generic over some state or ability.
5555+5656+### Example: forall
5757+5858+```rust
5959+use fx::{Fx, State};
6060+6161+let fx: Fx<'static, i32, i32> = State::<i32>::get();
6262+let fx2 = fx.forall(|fx| fx.map(|n| n + 1));
6363+let result = fx2.provide(41).eval();
6464+assert_eq!(result, 42);
6565+```
6666+6767+## provide_part: Partial Context Provision
6868+6969+`provide_part` allows you to provide only part of a context, leaving the rest to be provided later. This is useful for composing effects with partially known environments.
7070+7171+### Example: provide_part
7272+7373+```rust
7474+use fx::Fx;
7575+7676+let fx: Fx<'static, (i32, bool), i32> = Fx::value(7);
7777+let fx2 = fx.provide_part::<i32, bool>(1); // Now Fx<'static, bool, i32>
7878+```
7979+8080+## field_macro: Field-Based Lenses
8181+8282+The `field_macro` crate provides macros to automatically generate lenses for struct fields, making it easy to focus on and update nested fields in your state.
8383+8484+### Example: field_macro
8585+8686+```rust
8787+use field_macro::Field;
8888+8989+#[derive(Field, Clone)]
9090+struct AppState {
9191+ #[field]
9292+ counter: u32,
9393+ #[field]
9494+ user: User,
9595+}
9696+9797+// Now you can use AppState::counter() and AppState::user() as lenses.
9898+```
9999+100100+______________________________________________________________________
101101+102102+Lenses, struct-based contexts, and advanced combinators make fx-rs a powerful tool for modular, reusable, and testable stateful code.
+23
book/src/testing.md
···11+# Testing Effectful Code
22+33+fx-rs makes it easy to test effectful code by swapping in test handlers or mocks.
44+55+## Example: Mocking an Ability
66+77+```rust
88+use fx::Fx;
99+1010+trait Http { fn get(&self, url: &str) -> String; }
1111+struct MockHttp;
1212+impl Http for MockHttp { fn get(&self, url: &str) -> String { format!("mocked: {}", url) } }
1313+1414+fn fetch<'f, H: Http>(url: &'f str) -> Fx<'f, H, String> {
1515+ Fx::pure(move |h: &H| h.get(url))
1616+}
1717+1818+let fx = fetch("/test");
1919+let result = fx.run(&MockHttp);
2020+assert_eq!(result, "mocked: /test");
2121+```
2222+2323+You can also capture outputs, use property-based testing, and swap handlers for deterministic tests.
+31
book/src/uniqueness.md
···11+# What Makes fx-rs Unique
22+33+fx-rs is not just another effect system ported to Rust. It is designed from the ground up to take advantage of Rust's type system, ownership, and module system, while providing a modern, ergonomic, and composable effect API. Here are some of the features that make fx-rs stand out:
44+55+## 1. Programmable Handlers as First-Class Values
66+77+Handlers in fx-rs are values, not static typeclasses. This means you can pass, compose, and swap handlers at runtime, enabling scoped and modular effect interpretation. Handler replacement is explicit and type-safe, allowing for powerful patterns like test mocking, dependency injection, and context adaptation.
88+99+## 2. Trait-Based Abilities and Contexts
1010+1111+Abilities are defined as Rust traits, and the effect context is any Rust struct. This enables named, nested, and multiple abilities, and makes dependency management explicit and ergonomic. You can use Rust's trait system to define effectful operations, and context structs to group and manage abilities.
1212+1313+## 3. Composable Context Adaptation
1414+1515+fx-rs provides combinators like `adapt` and `contra_map` to adapt computations to different contexts. This allows for scoped dependency replacement, modular handler wiring, and seamless integration with existing Rust code.
1616+1717+## 4. Direct-Style and Builder APIs
1818+1919+While fx-rs supports traditional `map`/`flat_map` combinators, it also offers (and is evolving) direct-style macros and builder patterns for more ergonomic effectful code. This reduces boilerplate and makes effectful code look and feel like regular Rust.
2020+2121+## 5. Lenses and Fine-Grained State
2222+2323+State effects in fx-rs are modular and composable, with support for lenses to focus on subparts of state. This enables fine-grained, type-safe state manipulation and modular effect composition.
2424+2525+## 6. Structured Concurrency as Effects
2626+2727+Concurrency primitives (spawning, channels, async tasks) can be modeled as abilities, making concurrent operations explicit, composable, and testable. Handlers can swap between real and mock concurrency backends for robust testing.
2828+2929+## 7. Macro-Based Ergonomics
3030+3131+fx-rs leverages Rust's macro system (see the macro crates in the repository) to reduce boilerplate, derive abilities, and provide ergonomic APIs for effectful programming.