The Problem with Positional Types
Early effect code sometimes used tuples as R. That works briefly, then becomes fragile.
The tuple explosion
Two dependencies: readable.
#![allow(unused)] fn main() { Effect<A, E, (Database, Logger)> }
Five dependencies: which is which?
#![allow(unused)] fn main() { Effect<A, E, (Pool, Pool, Logger, Config, HttpClient)> // ^^^^ two Pools — main DB or cache? }
Tuples are positional. (Pool, Pool, …) is ambiguous when both fields share a type.
Fragility under change
#![allow(unused)] fn main() { fn foo() -> Effect<A, E, (Database, Logger)> // 0 1 }
A teammate inserts Config:
#![allow(unused)] fn main() { fn foo() -> Effect<A, E, (Database, Config, Logger)> // 0 1 2 }
Every caller that built (db, log) must become (db, config, log). The type system does not point at stale indices — it's a silent refactor hazard.
Same-type collision
Rust cannot distinguish a main-database Pool from a cache Pool in a tuple:
#![allow(unused)] fn main() { run_blocking(effect, (cache_pool, main_pool)); // compiles, wrong at runtime }
What we need
Each dependency needs a compile-time name independent of position:
Database→ the primaryPoolCache→ the cachePool
Different keys, same underlying type — the compiler catches swaps.
That's what `` generates.