Effect.ts: Absence as First-Class
Dependency Injection • Lesson 19 of 51
19. Services & Context - Providing Absent Dependencies
Remember the 'Requirements' in EffectA, E, R? Here's how to provide them.
Remember the 'Requirements' in
Effect<A, E, R>? Here's how to provide them.
Code Example
// Define a service interface
class Database extends Context.Tag("Database")<
Database,
{
query: (sql: string) => Effect<Result, DbError>
}
>() {}
// Use it (doesn't exist yet!)
const getUser = (id: string) =>
Effect.gen(function* () {
const db = yield* Database; // "I need Database"
return yield* db.query(`SELECT * WHERE id=${id}`);
});
// Type: Effect<User, DbError, Database>
// ^^^^^^^^
// Requires Database!
// Provide it (NOW it can run)
const program = getUser("123").pipe(
Effect.provide(DatabaseLive)
);
Interactive Example
const output: string[] = [];
output.push('class Database extends Context.Tag("Database") {...}');
output.push('');
output.push('Effect requires Database in type signature:');
output.push(' Effect<User, DbError, Database>');
output.push(' ^^^^^^^^');
output.push('');
output.push('Must provide before running:');
output.push(' getUser("123").pipe(Effect.provide(DatabaseLive))');
output.push('');
output.push(' Services are typed requirements!');
return output.join('
');
Explanation
Services: typing absent dependencies in the Effect signature.
Traditional approach: assume services exist (imports, globals). Effect types service unavailability:
- User is unavailable
- Database is unavailable (typed in
Effect<A,E,R>) - No assumptions about Database existing
- Must explicitly provide it
Absence-first dependency injection: services are typed as absent!
Part 19 of 51 in the Effect.ts Absence Modeling series