Effect-TS Primer
Reactive Agents is built on Effect-TS. You don’t need to be an Effect expert to use the framework, but understanding these concepts helps.
Effect<A, E, R>
Section titled “Effect<A, E, R>”An Effect is a description of a computation that:
- Succeeds with value
A - Fails with error
E - Requires services
R
import { Effect } from "effect";
// A simple Effect that succeedsconst hello = Effect.succeed("Hello, world!");
// An Effect that might failconst parse = (input: string): Effect.Effect<number, Error> => Effect.try(() => JSON.parse(input));
// An Effect that requires a serviceconst greet = Effect.gen(function* () { const agent = yield* AgentService; return yield* agent.getAgent("agent-1");});Layer<Out, Err, In>
Section titled “Layer<Out, Err, In>”A Layer is a recipe for constructing services:
- Provides service
Out - Might fail with
Err - Requires dependency
In
import { Layer, Context } from "effect";
// Define a serviceclass MyService extends Context.Tag("MyService")< MyService, { readonly greet: (name: string) => Effect.Effect<string> }>() {}
// Create a Layer that provides itconst MyServiceLive = Layer.succeed(MyService, { greet: (name) => Effect.succeed(`Hello, ${name}!`),});Context.Tag
Section titled “Context.Tag”Tags identify services in the Effect dependency injection system:
class AgentService extends Context.Tag("AgentService")< AgentService, { readonly createAgent: (config: AgentConfig) => Effect.Effect<Agent, AgentError>; readonly getAgent: (id: AgentId) => Effect.Effect<Agent, AgentNotFoundError>; }>() {}Schema
Section titled “Schema”Effect Schema provides runtime validation with TypeScript types:
import { Schema } from "effect";
const AgentConfig = Schema.Struct({ name: Schema.String, model: Schema.String, maxIterations: Schema.Number.pipe(Schema.between(1, 100)),});
type AgentConfig = typeof AgentConfig.Type;Data.TaggedError
Section titled “Data.TaggedError”Typed, pattern-matchable errors:
import { Data } from "effect";
class AgentNotFoundError extends Data.TaggedError("AgentNotFoundError")<{ readonly agentId: string;}> {}
// Pattern match on _tagconst handle = Effect.catchTag("AgentNotFoundError", (e) => Effect.succeed(`Agent ${e.agentId} not found`));Mutable state in a pure, concurrent-safe way:
import { Ref } from "effect";
const counter = yield* Ref.make(0);yield* Ref.update(counter, (n) => n + 1);const value = yield* Ref.get(counter);For Framework Users
Section titled “For Framework Users”If you’re using the ReactiveAgents.create() builder, you interact with standard async/await:
// No Effect knowledge needed!const agent = await ReactiveAgents.create() .withProvider("anthropic") .build();
const result = await agent.run("Hello!");The Effect-TS internals are only exposed when you need advanced control via buildEffect() and runEffect().