An interactive introduction to Effect
An interactive introduction to Effect
./start.sh
)“an
Effect
is a description of a program that is lazy and immutable”
type Function<Args, T> = (...args: Args) => T;
type SaferFunction<Args, T, E> = (...args: Args) => T | E;
declare function Foo(): "baz" | "bar" | Error;
const result = Foo();
const error = result instanceof Error;
declare function Foo2(): "baz" | "bar";
const result2 = Foo2();
const error = result2 !== "baz";
declare function Inner(): "baz" | "bar" | Error;
function Outer(): number | Error {
const result = Inner();
if (result instanceof Error) {
return result;
}
return result.length;
}
Effect
s are differenttype Effect<Value, Error = never> = /* unimportant */;
declare const foo: Effect<number, never>;
// also the same as Effect<number>
declare const bar: Effect<number, Error>;
import { db } from "./db";
function getUser(id: number): User {
return db.users.getById(id);
}
function getUser(db: DataBase, id: number): User {
return db.users.getById(id);
}
function updateEmail(
db: DataBase,
logger: Logger,
telemetry: Telemetry,
id: number,
newEmail: string
): User {
const user = db.users.getById(id);
db.users.updateEmail(id, newEmail);
logger.info(`Updated email for user ${id}`);
telemetry.record("email-updated", { id });
return user;
}
type Effect<Value, Error = never, Requirement = never>
declare const getUser: Effect<User, NotFoundError, DataBase>;
declare const updateEmail: Effect<
User,
NotFoundError,
DataBase | Logger | Telemetry
>;
const runnable: Effect<User, NotFoundError, never> =
provideDependencies(getUser, db);
const testable: Effect<User, NotFoundError, never> =
provideDependencies(getUser, mockDb);
declare async function getUser(id: number): Promise<User>;
declare function getUser(id: number):
Effect<User, NotFoundError, UserRepo>;
const foo = () => Date.now();
console.log(foo); // [Function: foo]
console.log(foo()); // 1707076796922
const effectFoo = Effect.sync(() => Date.now());
console.log(effectFoo);
// { _id: 'Effect', _op: 'Sync', i0: [Function: foo] }
console.log(Effect.runSync(effectFoo)); // 1707076796922
Effect
sasync, asyncEffect, asyncEither, asyncOption, die, dieMessage, dieSync, fail, failCause, failCauseSync, failSync, gen, never, none, promise, succeed, succeedNone, succeedSome, suspend, sync, unit, withClockScoped, withConsoleScoped, yieldNow
succeed
/ fail
for valuessync
/ try
for sync functionspromise
/ tryPromise
for async functionsasync
for async callbacksEffect
srunSync
for synchronous effectsrunPromise
for asynchronous effectsrunSyncExit
/ runPromiseExit
for geting the error as a value, instead of thrownconst getDate = () => Date.now();
const double = (x) => x * 2;
const doubleDate = () => {
const date = getDate();
return double(date);
};
const getDate = Effect.sync(() => Date.now());
const double = (x) => x * 2;
const doubleDate = Effect.sync(() => {
const date = Effect.runSync(getDate);
return double(date);
});
Effect
s at the EDGES of your programmap
: Transform the value of an effectflatMap
: Transform the value of an effect into another effecttap
: Perform a side effect without changing the valueall
: Merge multiple effects into a single effectcatchAll
: Recover from all errorscatchTag
: Recover from a specific errormapError
: Transform the error of an effectmatch
: Handle both caseseither
: Move the error into the success channelContext.Tag
created a placehold for a service that can be used in an effect as if it were the real thingprovideService(Tag, implementation)
provides the service to the effectLayer
s are programs that create services and run before effects that require themprovide(Tag, layer)
provides the layer to the effectScope
contains 'finalizers' that are run when the scope is closedScope
service it means: "I have some resources that need to be cleaned up at some point"Scope
indicates where the scope should be closedacquireRelease
: a helper for created scoped resourcesOption<T>
: A value that may or may not existEither<A, E>
: A disjointed union of two typesExit<A, E>
: Essentially a Either<A, Cause<E>>
Cause<E>
: The result of a computationDuration
: A time intervalChunk<T>
: An immutable, functional arrayHashSet<T> / HashMap<K, V>
: Immutable, functional collections that support custom hashing and equality with Equal
and Hash
Data
: A module for auto-implementing Equal
and Hash
for dataSchema<A, I, R>
can:unknown
data is of type A
unknown
data is of type I
A
to I
I
to R
Schema
is incredibly powerful@effect/platform
@effect/cli
Schedule
is a description of a series of delaysConfig
can be used just like an effect to get the value or a ConfigError
ConfigProvider
Stream
is an Effect that may produce none, one, or many valuesEffect
, most of the same combinatorsSink
type for more advanced and resuable consuming of streamsconst normalJS = () => {
let i = 0;
setInterval(() => console.log("i", i), 250);
while (true) {
i++;
}
};
Why doesn’t this work?
const effect = Effect.gen(function* (_) {
let i = 0;
yield* _(
Effect.suspend(() => Console.log("i", i)),
Effect.repeat(Schedule.spaced(250)),
Effect.fork
);
while (true) {
yield* _(Effect.sync(() => i++));
}
});
(if you dont have to)
const program = Effect.gen(function* (_) {
let i = 0;
yield* _(
Effect.suspend(() => Console.log("i", i)),
Effect.repeat(Schedule.spaced(250)),
Effect.fork
);
i = 100;
});
Effect.runPromise(program);
forkDaemon
spawns a fiber in the top level, global scopeforkScoped
spawns a fiber and requires a Scope
to be providedforkIn
allows you specify a custom scope to spawn a fiber in with a Scope
Deferred
for 'one shot' channel that can errorQueue
standard 'channel', customizable back pressure behaviorPubSub
for 'publishing' to multiple 'subscribers'requestIdleCallback
STM
is its own little Effect
-like world, has most of the same combinators as EffectsSTM
there should be no side effects, only logic and operating on STM data structuresT_
versions of most common data structurescommit
ing an stm transaction executes it in a single opaque operationT_
data structures to be immutableRuntime<R>
is a thing that can run an Effect
to an Exit
Context
of services that can be seeded with some initial services (the R
)FiberRef
is a mutable value that is local to a fiber (runtimes start with a bunch of built in ones but you can create your own)RuntimeFlags
ultra low level bitflags for controlling runtime behaviorSchema
Request
s and RequestResolver
s for those Request
sEffect.request