Cheat sheet
Every VibeLang change next to the Effect code it replaces. The left column is Effect 4 TypeScript, and the right column is VibeLang. The specification states the rules behind each change. The examples share these declarations:
// run: Console.log(JSON.stringify([getUser("1"), getOrders("1"), handle(() => release("v1"), Approval, { approve: (_request, k) => resume(k, true) })]))
error NotFound { id: string }
interface User { id: string; name: string }
function getUser(id: string): User!NotFound {
sleep("5 millis")
if (id === "missing") throw new NotFound({ id })
return { id, name: "Ada" }
}
function getOrders(id: string): number!! {
return id.length
}
control Approval {
approve(request: string): boolean!!
}
function release(version: string) {
return Approval.approve(`deploy ${version}`) ? `released ${version}` : "declined"
}
// Output: [{"id":"1","name":"Ada"},1,"released v1"]Types in this example
error- class NotFound
NotFound- class NotFound
id- (property) id: string
User- interface User
id- (property) User.id: string
name- (property) User.name: string
getUser- function getUser(id: string): User!NotFound!
id- (parameter) id: string
sleep- const sleep: (duration: Duration.Input) => void!!Runs here and returns
void.Returns an effect that suspends the current fiber for the specified duration without blocking a JavaScript thread. NotFound- constructor NotFound<{ readonly id: string; }>(args: { readonly id: string; }): NotFound
function- function getOrders(id: string): number!!
getOrders- function getOrders(id: string): numberPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<number, never, never>. length- (property) String.length: numberReturns the length of a String object.
control- abstract class Approval
Approval- abstract class Approval
approve- (method) Approval.approve(request: string): boolean!!
request- (parameter) request: string
release- function release(version: string): string!!Approval
version- (parameter) version: string
approve- (method) Approval.approve(request: string): boolean!!Runs here and returns
boolean.
Types
Effect types (D127). An empty slot is never. Dependencies left out are inferred from a function's body, and are never anywhere else.
type Find = (id: string) => Effect.Effect<User, NotFound>
type Release = (version: string) => Effect.Effect<string, never, Approval>
type Load = () => Effect.Effect<string>
const hello = (id: string): Effect.Effect<string, NotFound> =>
Effect.map(getUser(id), (user) => `Hello, ${user.name}`)// run: Console.log(hello("1"))
type Find = (id: string) => User!NotFound
type Release = (version: string) => string!!Approval
type Load = () => string!!
function hello(id: string): string!NotFound {
return `Hello, ${getUser(id).name}`
}
// Output: Hello, AdaTypes in this example
Find- type Find = (id: string) => User!NotFound!
id- (parameter) id: string
User- (alias) interface User
NotFound- (alias) class NotFound
Release- type Release = (version: string) => string!!Approval
version- (parameter) version: string
Approval- (alias) abstract class Approval
Load- type Load = () => string!!
hello- function hello(id: string): string!NotFound!
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. name- (property) User.name: string
Declarations
Errors (D126).
class Missing extends Data.TaggedError("Missing")<{ readonly id: string }> {}
class Timeout extends Data.TaggedError("Timeout") {}error Missing { id: string }
error TimeoutTypes in this example
error- class Missing
Missing- class Missing
id- (property) id: string
error- class Timeout
Timeout- class Timeout
Services (D128, D223). Members have no bodies, and value members are readonly.
class Users extends Context.Service<Users, {
find(id: string): Effect.Effect<User, NotFound>
readonly region: string
}>()("app/Users") {}service Users {
find(id: string): User!NotFound
region: string
}Types in this example
service- abstract class Users
Users- abstract class Users
find- (method) Users.find(id: string): User!NotFound!
id- (parameter) id: string
User- (alias) interface User
NotFound- (alias) class NotFound
region- (property) Users.region: string
Control operations (D128, D223). Effect has none, so the left column is VibeLang's long form.
abstract class Review extends Control {
abstract approve(request: string): Effect<boolean, never, never>
}Types in this example
Review- abstract class Review
Control- (alias) abstract class ControlD2: control operations are abstract classes that
extend Control; the compiler rewrites them. approve- (method) Review.approve(request: string): boolean!!
request- (parameter) request: string
Effect- type A!E = never!R = never = A!E!RD4: in a .vibe file,
Effect<A, E, R>isEffect.Effect<A, E, R>.
control Review {
approve(request: string): boolean!!
}Types in this example
control- abstract class Review
Review- abstract class Review
approve- (method) Review.approve(request: string): boolean!!
request- (parameter) request: string
Layers (D221). A layer is Effect's own call: the block after Layer.effect(Users) is its effect (block params, D188), its statements run once when the layer builds, and the service types the returned members. Without setup, write Layer.succeed(Users, { ... }).
const UsersLive = Layer.effect(Users, Effect.gen(function* () {
const seen = new Set<string>()
return Users.of({ find: (id) => getUser(id), region: "eu" })
}))// run: provide UsersLive { Console.log(JSON.stringify(Users.find("1"))); Console.log(Users().region) }
const UsersLive = Layer.effect(Users) {
const seen = new Set<string>()
return {
find(id) { return getUser(id) },
region: "eu"
}
}
// Output: {"id":"1","name":"Ada"}
// Output: euTypes in this example
UsersLive- const UsersLive: Layer<Users, never, never>Layer: provides
Users; needs nothing. Layer- (alias) module "effect/Layer"
effect- const effect: <Users, { find(id: string): User!NotFound!; region: string; }, never, never>(service: Key<Users, { find(id: string): User!NotFound!; region: string; }>, effect: { find(id: string): User!NotFound!; region: string; }!!) => Layer<Users, never, never>Constructs a layer from an effect that produces a single service.
Users- (alias) abstract class Users
seen- const seen: Set<string>
Set- var Set: new <string>(iterable?: Iterable<string> | null | undefined) => Set<string>
find- (method) find(id: string): User!NotFound!
id- (parameter) id: string
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. region- (property) region: string
Schemas (D133). One declaration gives the schema and its type.
const Account = Schema.Struct({
id: Schema.String,
nickname: Schema.optional(Schema.String),
role: Schema.Literals(["admin", "member"])
})
interface Account extends Schema.Schema.Type<typeof Account> {}// run: Console.log(JSON.stringify(Schema.decodeUnknownEffect(Account)({ id: "1", role: "admin" })))
schema Account {
id: string
nickname?: string
role: "admin" | "member"
}
// Output: {"id":"1","role":"admin"}Types in this example
Account- const Account: Struct<{ readonly id: String; readonly nickname: optional<String>; readonly role: Literals<readonly ["admin", "member"]>; }>
id- (property) id: String
nickname- (property) nickname: optional<String>
role- (property) role: Literals<readonly ["admin", "member"]>
Readonly by default (D134). Write mutable for a member you assign.
interface Cart { readonly id: string; items: Array<string> }interface Cart { id: string; mutable items: Array<string> }Types in this example
Cart- interface Cart
id- (property) Cart.id: string
items- (property) Cart.items: string[]
Array- interface Array<T>
Programs
Statements instead of generators. Calls run; there is no yield*.
const greetUser = Effect.fn("greetUser")(function* (id: string) {
const user = yield* getUser(id)
yield* Effect.Console.log(`Hello, ${user.name}`)
return user.name
})// run: Console.log(Effect.provide(() => greetUser("1"), Logger.layer([sync(() => Logger.make(({ message }) => console.log(...(message as unknown[]))))])))
function greetUser(id: string) {
const user = getUser(id)
Console.log(`Hello, ${user.name}`)
return user.name
}
// Output: Hello, Ada
// Output: AdaTypes in this example
greetUser- function greetUser(id: string): string!NotFound!
id- (parameter) id: string
user- const user: User
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. Console- (alias) module "effect/Console"
log- const log: (...args: ReadonlyArray<any>) => void!!Runs here and returns
void.Logs a general-purpose message to the console. name- (property) User.name: string
Programs passed by name (D147). A call runs where it stands, and is passed as a program where a parameter expects one.
const user = yield* getUser("1")
const first = yield* Effect.race(getUser("1"), getUser("2"))// run: Console.log(fastest().name)
function fastest() {
const user = getUser("1")
return race(getUser("1"), getUser("2"))
}
// Output: AdaTypes in this example
fastest- function fastest(): User!NotFound!
user- const user: User
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. race- const race: <User, NotFound, never, User, NotFound, never>(self: User!NotFound!, that: User!NotFound!, options?: {
readonly onWinner?: (options: {
readonly fiber: Fiber<any, any>;
readonly index: number;
readonly parentFiber: Fiber<any, any>;
}) => void;
}) => User!NotFound!Runs here and returns
User.Races two effects and returns the first successful result. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
raceas a program: it runs when that program runs, not here, and returnsUser.
Providing layers (D130). return inside the block returns from the function.
const program = Effect.gen(function* () {
const users = yield* Users
return yield* users.find("1")
}).pipe(Effect.provide(UsersLive))// run: Console.log(JSON.stringify(program()))
function program() {
provide UsersLive {
return Users.find("1")
}
}
// Output: {"id":"1","name":"Ada"}Types in this example
program- function program(): User!NotFound!
UsersLive- (alias) const UsersLive: Layer<Users, never, never>Layer: provides
Users; needs nothing. Users- (alias) abstract class Users
find- (method) Users.find(id: string): User!NotFound!Runs here and returns
User.
Providing one service (D173, D174). provide Key = value provides one implementation without a layer; layers and services mix in one list, and an effect module such as Random stands for its key, Random.Random.
const seeded = { nextIntUnsafe: () => 4, nextDoubleUnsafe: () => 0.5 }
const roll = Random.nextInt.pipe(
Effect.provideService(Random.Random, seeded),
Effect.provide(Logger.layer([]))
)// run: Console.log(roll())
const seeded = { nextIntUnsafe: () => 4, nextDoubleUnsafe: () => 0.5 }
function roll(): number!! {
provide Logger.layer([]), Random = seeded {
return Random.nextInt()
}
}
// Output: 4Types in this example
seeded- const seeded: { nextIntUnsafe: () => number; nextDoubleUnsafe: () => number; }
nextIntUnsafe- (property) nextIntUnsafe: () => number
nextDoubleUnsafe- (property) nextDoubleUnsafe: () => number
roll- function roll(): number!!
Logger- (alias) module "effect/Logger"
layer- const layer: <readonly []>(loggers: readonly [], options?: {
readonly mergeWithExisting?: boolean | undefined;
} | undefined) => Layer<never, never, never>Creates a
Layerwhich will overwrite the current set of loggers with the specified array ofloggers. Random- (alias) module "effect/Random"Provides the service
Random.Randomto this provide block's statements. Random- (alias) module "effect/Random"
nextInt- const nextInt: () => number!!Runs here and returns
number.Generates a random integer betweenNumber.MIN_SAFE_INTEGER(inclusive) andNumber.MAX_SAFE_INTEGER(inclusive).
Catching by tag (D216). Catch the failure and match on it. A match that names every failure needs no rethrow; default: throw e passes on the ones it does not name.
const describe = (id: string) =>
Effect.map(getUser(id), (user) => user.name).pipe(
Effect.catchTag("NotFound", (e) => Effect.succeed(`no user ${e.id}`))
)// run: Console.log(JSON.stringify([describe("1"), describe("missing")]))
function describe(id: string) {
try {
return getUser(id).name
} catch (e) {
return match (e) {
when NotFound: `no user ${e.id}`;
}
}
}
// Output: ["Ada","no user missing"]Types in this example
describe- function describe(id: string): string!!
id- (parameter) id: string
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. name- (property) User.name: string
e- var e: NotFound
NotFound- (alias) class NotFound
id- (property) id: string
Falling back in an expression (D236). x catch fallback runs the fallback only after an expected failure; defects and interrupts pass through.
const nameOrGuest = (id: string) =>
Effect.map(getUser(id), (user) => user.name).pipe(
Effect.orElseSucceed(() => "guest")
)// run: Console.log(JSON.stringify([nameOrGuest("1"), nameOrGuest("missing")]))
function nameOrGuest(id: string) {
return getUser(id).name catch "guest"
}
// Output: ["Ada","guest"]Types in this example
nameOrGuest- function nameOrGuest(id: string): string!!
id- (parameter) id: string
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. name- (property) User.name: string
catch- catchRuns the left side and, after an expected failure (
NotFound), the fallback instead (Effect.orElseSucceed). Returnsstring. Defects and interrupts pass through.
Catching in an expression (D236). x catch (e) => … passes the failure to a handler, which can match on it; a match that names every failure removes them all.
const nameOrReason = (id: string) =>
Effect.map(getUser(id), (user) => user.name).pipe(
Effect.catch((e) => Effect.succeed(`no user ${e.id}`))
)// run: Console.log(JSON.stringify([nameOrReason("1"), nameOrReason("missing")]))
function nameOrReason(id: string) {
return getUser(id).name catch (e) => match (e) {
when NotFound: `no user ${e.id}`;
}
}
// Output: ["Ada","no user missing"]Types in this example
nameOrReason- function nameOrReason(id: string): string!!
id- (parameter) id: string
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. name- (property) User.name: string
catch- catchRuns the left side and passes an expected failure,
NotFound, to the handler (Effect.catch). Returnsstring. Defects and interrupts pass through. e- (parameter) e: NotFound
NotFound- (alias) class NotFound
id- (property) id: string
Handling control operations (D132). Effect has none, so the left column is VibeLang's long form.
// run: Console.log(auto("v1"))
function auto(version: string) {
return handle(() => release(version), Approval, {
approve(_request, continuation) {
return resume(continuation, true)
}
})
}
// Output: released v1Types in this example
auto- function auto(version: string): string!!
version- (parameter) version: string
handle- function handle<string, never, Approval, Approval, Approval, {
approve(_request: string, continuation: Continuation<boolean, never, string>): string!!;
}>(program: string!!Approval, control: Key<Approval, Approval>, clauses: {
approve(_request: string, continuation: Continuation<boolean, never, string>): string!!;
}): string!!Runs here and returns
string.handle(program, Control, clauses, onReturn?): runsprogramwith a deep handler forControl's operations. Each clause receives the operation's arguments andk, the rest of the program. The result is what the program returns (throughonReturnwhen given) or what a clause returns without resuming. Inside a clause,resume(k, v)has the program's own result type; withonReturnit has typenever, so a clause's tailresumeadds nothing to the result (D97). release- (alias) function release(version: string): string!!ApprovalRuns here and returns
string. Approval- (alias) abstract class Approval
approve- (method) approve(_request: string, continuation: Continuation<boolean, never, string>): string!!
_request- (parameter) _request: string
continuation- (parameter) continuation: Continuation<boolean, never, string>
resume- const resume: <boolean, never, string>(k: Continuation<boolean, never, string>, value: boolean) => string!!Runs here and returns
string.Continues the program withvalueat the perform site; returns what thehandlethen produces.
// run: Console.log(auto("v1"))
function auto(version: string) {
try {
return release(version)
} handle Approval.approve(request) {
resume(true)
}
}
// Output: released v1Types in this example
auto- function auto(version: string): string!!
version- (parameter) version: string
release- (alias) function release(version: string): string!!ApprovalRuns here and returns
string. Approval- (alias) abstract class Approval
approve- (method) Approval.approve(request: string): boolean!!
request- const request: string
resume- const resume: <boolean, never, never>(k: Continuation<boolean, never, never>, value: boolean) => !!Runs here and returns
never.Continues the program withvalueat the perform site; returns what thehandlethen produces.
Entry point (D135). An exported main runs when Node starts its module, and not when a test imports it.
// main.ts
export const main = Effect.log("Hello")
// run.ts
NodeRuntime.runMain(main)// run: Effect.provide(main(), Logger.layer([sync(() => Logger.make(({ message }) => console.log(...(message as unknown[]))))]))
export function main() {
log("Hello")
}
// Output: HelloTypes in this example
main- function main(): void!!
log- const log: (...message: ReadonlyArray<any>) => void!!Runs here and returns
void.Logs one or more messages using the default log level.
Causes, exits, and interruption
Catch every cause (D217). match on exit(…): a Failure exit's cause also holds defects and interrupts.
const safe = (id: string) =>
Effect.map(getUser(id), (user) => user.name).pipe(
Effect.catchCause((cause) => Effect.succeed(`failed: ${Cause.pretty(cause)}`))
)// run: Console.log(safe("1")); Console.log(safe("missing").includes("NotFound"))
function safe(id: string) {
return match (exit(getUser(id))) {
when Exit.Success(let user): user.name;
when Exit.Failure(let cause): `failed: ${Cause.pretty(cause)}`;
}
}
// Output: Ada
// Output: trueTypes in this example
safe- function safe(id: string): string!!
id- (parameter) id: string
exit- const exit: <User, NotFound, never>(self: User!NotFound!) => Exit<User, NotFound>!!Runs here and returns
Exit<User, NotFound>.Transforms an effect to encapsulate both failure and success using theExitdata type. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
exitas a program: it runs when that program runs, not here, and returnsUser. user- let user: User
name- (property) User.name: string
cause- let cause: Cause<NotFound>
Cause- (alias) module "effect/Cause"
pretty- const pretty: <NotFound>(cause: Cause<NotFound>) => stringFormats a
Causeas a human-readable string for logging or debugging.
Rethrow a cause (D177). throw with a Cause fails with every reason it holds.
const logged = getUser("1").pipe(
Effect.catchCause((cause) => Effect.andThen(Effect.logError(cause), Effect.failCause(cause)))
)// run: Console.log(JSON.stringify(logged()))
function logged() {
const outcome = exit(getUser("1"))
if (outcome is Exit.Failure(let cause)) {
logError(cause)
throw cause
}
return outcome.value
}
// Output: {"id":"1","name":"Ada"}Types in this example
logged- function logged(): User!NotFound!
outcome- const outcome: Exit<User, NotFound>
exit- const exit: <User, NotFound, never>(self: User!NotFound!) => Exit<User, NotFound>!!Runs here and returns
Exit<User, NotFound>.Transforms an effect to encapsulate both failure and success using theExitdata type. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
exitas a program: it runs when that program runs, not here, and returnsUser. cause- let cause: Cause<NotFound>
logError- const logError: (...message: ReadonlyArray<any>) => void!!Runs here and returns
void.Logs one or more messages at the ERROR level. outcome- const outcome: Success<User, NotFound>
value- (property) Success<User, NotFound>.value: User
Run on every exit (D178). The block runs without interruption; a success carries no value.
const tracked = getUser("1").pipe(
Effect.onExit((exit) => Effect.log(`lookup ${exit._tag}`))
)// run: Console.log(Effect.provide(() => tracked().name, Logger.layer([sync(() => Logger.make(({ message }) => console.log(...(message as unknown[]))))])))
function tracked() {
try {
return getUser("1")
} finally (exit) {
log(`lookup ${exit._tag}`)
}
}
// Output: lookup Success
// Output: AdaTypes in this example
tracked- function tracked(): User!NotFound!
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. exit- const exit: Failure<void, NotFound> | Success<void, NotFound>
log- const log: (...message: ReadonlyArray<any>) => void!!Runs here and returns
void.Logs one or more messages using the default log level. _tag- (property) _tag: "Failure" | "Success"
Exit, yield, and interrupt (D179). exit takes its program by name.
const result = yield* Effect.exit(getUser("1"))
yield* Effect.yieldNow
if (result._tag === "Failure") yield* Effect.interrupt// run: Console.log(outcome()._tag)
function outcome() {
const result = exit(getUser("1"))
yieldNow()
if (result is Exit.Failure) interrupt()
return result
}
// Output: SuccessTypes in this example
outcome- function outcome(): Exit<User, NotFound>!!
result- const result: Exit<User, NotFound>
exit- const exit: <User, NotFound, never>(self: User!NotFound!) => Exit<User, NotFound>!!Runs here and returns
Exit<User, NotFound>.Transforms an effect to encapsulate both failure and success using theExitdata type. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
exitas a program: it runs when that program runs, not here, and returnsUser. yieldNow- const yieldNow: (priority?: number) => void!!Runs here and returns
void.BareyieldNow()(D179): yields to the scheduler, with an optional priority. interrupt- const interrupt: () => !!Runs here and returns
never.Bareinterrupt()(D179): interrupts the running fiber, as Effect.interrupt.
Interruption (D180). The blocks keep return and break meaning what they mean in the function; uninterruptible(program) also works.
const guarded = Effect.uninterruptibleMask((restore) =>
Effect.gen(function* () {
const user = yield* getUser("1")
const orders = yield* restore(getOrders(user.id))
return { user, orders }
})
)
const cancellable = Effect.interruptible(getOrders("1"))// run: Console.log(JSON.stringify([guarded(), cancellable()]))
function guarded() {
uninterruptible {
const user = getUser("1")
restore {
return { user, orders: getOrders(user.id) }
}
}
}
function cancellable() {
interruptible {
return getOrders("1")
}
}
// Output: [{"user":{"id":"1","name":"Ada"},"orders":1},1]Types in this example
guarded- function guarded(): { user: User; orders: number; }!NotFound!
user- const user: User
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. user- (property) user: User
orders- (property) orders: number
getOrders- function getOrders(id: string): numberPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<number, never, never>. id- (property) User.id: string
cancellable- function cancellable(): number!!
Calls
A tagged template is a call of its tag (D267). If sql returns an Effect, the statement sql`CREATE TABLE example (id TEXT)` runs it, and const rows = sql`SELECT * FROM example` receives its success value. Plain tags keep JavaScript behavior. A discarded structured Effect subtype, such as SQL Statement, reports EK4301: call the result explicitly, or annotate the tag as returning Effect<A, E, R>. A parameter that expects a program takes an Effectful template by name, just as it takes a call (D147).
No imports from effect (D146).
import { Console } from "effect"
const ready = Console.log("ready")// run: ready()
function ready() {
Console.log("ready")
}
// Output: readyTypes in this example
ready- function ready(): void!!
Console- (alias) module "effect/Console"
log- const log: (...args: ReadonlyArray<any>) => void!!Runs here and returns
void.Logs a general-purpose message to the console.
Common functions without the Effect. prefix (D151): sleep, log, retry, repeat, timeout, race, all, forEach, die, withSpan, and the log levels.
const patient = Effect.gen(function* () {
yield* Effect.sleep("10 millis")
return yield* Effect.timeout(Effect.retry(getUser("1"), Schedule.recurs(2)), "1 second")
})// run: Console.log(JSON.stringify(patient()))
function patient() {
sleep("10 millis")
return timeout(retry(getUser("1"), Schedule.recurs(2)), "1 second")
}
// Output: {"id":"1","name":"Ada"}Types in this example
patient- function patient(): User!NotFound | TimeoutError!
sleep- const sleep: (duration: Duration.Input) => void!!Runs here and returns
void.Returns an effect that suspends the current fiber for the specified duration without blocking a JavaScript thread. timeout- const timeout: <User, NotFound, never>(self: User!NotFound!, duration: Duration.Input) => User!NotFound | TimeoutError!Runs here and returns
User.Adds a time limit to an effect, triggering a timeout if the effect exceeds the duration. retry- const retry: <User, NotFound, never, number, never, never>(self: User!NotFound!, policy: Schedule<number, NotFound, never, never>) => User!NotFound!Passed by name to
timeoutas a program: it runs when that program runs, not here, and returnsUser.Retries typed failures from an effect according to a retry policy. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
retryas a program: it runs when that program runs, not here, and returnsUser. Schedule- (alias) module "effect/Schedule"
recurs- const recurs: (times: number) => Schedule<number>Returns a
Schedulewhich can only be stepped the specified number oftimesbefore it terminates.
Methods on Effect's data types (D152).
const hot = Stream.take(
Stream.map(Stream.filter(Stream.make(18, 25, 30), (c) => c >= 25), (c) => `${c} C`),
2
)// run: Console.log(JSON.stringify(Stream.runCollect(hot())))
function hot() {
return Stream.make(18, 25, 30).filter((c) => c >= 25).map((c) => `${c} C`).take(2)
}
// Output: ["25 C","30 C"]Types in this example
function- function hot(): Stream<string, never, never>!!
hot- function hot(): Stream<string, never, never>Plain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<Stream<string, never, never>, never, never>. Stream- (alias) module "effect/Stream"
make- const make: <readonly [18, 25, 30]>(values_0: 18, values_1: 25, values_2: 30) => Stream<18 | 25 | 30, never, never>Creates a stream from a sequence of values.
filter- (property) filter: <18 | 25 | 30, never, never>(self: Stream<18 | 25 | 30, never, never>, predicate: Predicate<18 | 25 | 30>) => Stream<18 | 25 | 30, never, never>Method syntax: calls
Stream.filter(Stream.make(18, 25, 30), …).Filters a stream to the elements that satisfy a predicate. c- (parameter) c: 18 | 25 | 30
map- (property) map: <18 | 25 | 30, never, never, string>(self: Stream<18 | 25 | 30, never, never>, f: (a: 18 | 25 | 30, i: number) => string) => Stream<string, never, never>Method syntax: calls
Stream.map(Stream.make(18, 25, 30).filter((c) => c >= 25), …).Transforms the elements of this stream using the supplied function. take- (property) take: <string, never, never>(self: Stream<string, never, never>, n: number) => Stream<string, never, never>Method syntax: calls
Stream.take(Stream.make(18, 25, 30).filter((c) => c >= 25).map((c) =>${c} C), …).Takes the firstnelements from this stream.
The pipe operator (D154). % is the value on the left; where it lands in a program parameter, the left side is passed as a program.
const piped = getUser("1").pipe(
Effect.retry(Schedule.recurs(2)),
Effect.timeout("1 second")
)// run: Console.log(JSON.stringify(piped()))
function piped() {
return getUser("1") |> retry(%, Schedule.recurs(2)) |> timeout(%, "1 second")
}
// Output: {"id":"1","name":"Ada"}Types in this example
piped- function piped(): User!NotFound | TimeoutError!
getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
retryas a program: it runs when that program runs, not here, and returnsUser. retry- const retry: <User, NotFound, never, number, never, never>(self: User!NotFound!, policy: Schedule<number, NotFound, never, never>) => User!NotFound!Passed by name to
timeoutas a program: it runs when that program runs, not here, and returnsUser.Retries typed failures from an effect according to a retry policy. %- (topic) %: User!NotFound!Passed by name to
retryas a program:getUser("1")runs when that program runs, not here. Schedule- (alias) module "effect/Schedule"
recurs- const recurs: (times: number) => Schedule<number>Returns a
Schedulewhich can only be stepped the specified number oftimesbefore it terminates. timeout- const timeout: <User, NotFound, never>(self: User!NotFound!, duration: Duration.Input) => User!NotFound | TimeoutError!Runs here and returns
User.Adds a time limit to an effect, triggering a timeout if the effect exceeds the duration. %- (topic) %: User!NotFound!Passed by name to
timeoutas a program:getUser("1") |> retry(%, Schedule.recurs(2))runs when that program runs, not here.
TC39 forms
Throw in an expression (D186, throw expressions).
const findUser = (users: Map<string, User>, id: string) =>
Effect.fromNullishOr(users.get(id)).pipe(Effect.mapError(() => new NotFound({ id })))// run: Console.log(findUser(new Map([["1", { id: "1", name: "Ada" }]]), "1").name); Console.log(Effect.result(findUser(new Map<string, User>(), "missing"))._tag)
function findUser(users: Map<string, User>, id: string) {
return users.get(id) ?? throw new NotFound({ id })
}
// Output: Ada
// Output: FailureTypes in this example
findUser- function findUser(users: Map<string, User>, id: string): User!NotFound!
users- (parameter) users: Map<string, User>
Map- interface Map<K, V>
User- (alias) interface User
id- (parameter) id: string
get- (method) Map<string, User>.get(key: string): User | undefinedReturns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.
NotFound- (alias) constructor NotFound<{ readonly id: string; }>(args: { readonly id: string; }): NotFound
id- (property) id: string
Blocks as values (D187, do expressions). The value is the last statement's.
const label = (id: string) =>
Effect.map(getUser(id), (user) => (user.name.length > 3 ? user.name : "short"))// run: Console.log(label("1"))
function label(id: string) {
const name = do {
const user = getUser(id)
if (user.name.length > 3) user.name
else "short"
}
return name
}
// Output: shortTypes in this example
label- function label(id: string): string!NotFound!
id- (parameter) id: string
name- const name: string
user- const user: User
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. name- (property) User.name: string
length- (property) String.length: numberReturns the length of a String object.
Blocks after calls (D188, block params). The block is the call's program; it needs return for a value.
const loaded = Effect.withSpan(
Effect.gen(function* () {
const user = yield* getUser("1")
return user.name
}),
"load"
)
it.live("finds a user", () =>
Effect.gen(function* () {
const user = yield* getUser("1")
assert.strictEqual(user.name, "Ada")
}))// run: Console.log(loaded())
function loaded() {
return withSpan("load") {
const user = getUser("1")
return user.name
}
}
// Output: AdaTypes in this example
loaded- function loaded(): string!NotFound!
withSpan- const withSpan: <string, NotFound, never>(name: string, self: string!NotFound!) => string!NotFound!Runs here and returns
string.Wraps the effect with a child span for tracing. user- const user: User
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. name- (property) User.name: string
// block-params.test.vibe
import { assert, it } from "@effect/vitest"
it.live("finds a user") {
const user = getUser("1")
assert.strictEqual(user.name, "Ada")
}
// Output:Types in this example
assert- (alias) const assert: Chai.Assert
it- (alias) const it: Vitest.Methods<never, {}>Vitest test API extended with Effect test helpers.
live- (property) Vitest.Methods<never, {}>.live: <void, NotFound>(name: string, self: Vitest.TestFunction<void, NotFound, Scope, [TestContext]>, timeout?: number | TestOptions) => void
user- const user: User
getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. strictEqual- (method) Chai.Assert.strictEqual<string>(actual: string, expected: string, message?: string): voidAsserts strict equality (===) of actual and expected.
name- (property) User.name: string
it.live uses the live clock because this page's getUser sleeps. it.effect supplies a test clock; a test that sleeps there must advance it with TestClock.adjust.
Pattern matching
From the TC39 pattern matching proposal (stage 1). See Pattern matching with match and is.
Match a value (D206, D209). Without default, a missing case of a finite union is EK4701, as with Match.exhaustive.
const phaseLabel = (phase: "draft" | "published") =>
Match.value(phase).pipe(
Match.when("draft", () => "Draft"),
Match.when("published", () => "Published"),
Match.exhaustive
)// run: Console.log(JSON.stringify([phaseLabel("draft"), phaseLabel("published")]))
function phaseLabel(phase: "draft" | "published") {
return match (phase) {
when "draft": "Draft";
when "published": "Published";
}
}
// Output: ["Draft","Published"]Types in this example
function- function phaseLabel(phase: "draft" | "published"): "Draft" | "Published"!!
phaseLabel- function phaseLabel(phase: "draft" | "published"): "Draft" | "Published"Plain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<"Draft" | "Published", never, never>. phase- (parameter) phase: "draft" | "published"
Match errors by class (D215). An error class is a pattern; and { let id } binds a property.
const describeFailure = (failure: NotFound | Timeout) =>
Match.value(failure).pipe(
Match.tag("NotFound", (e) => `no user ${e.id}`),
Match.tag("Timeout", () => "retry"),
Match.exhaustive
)// run: Console.log(JSON.stringify([describeFailure(new NotFound({ id: "7" })), describeFailure(new Timeout())]))
function describeFailure(failure: NotFound | Timeout) {
return match (failure) {
when NotFound and { let id }: `no user ${id}`;
when Timeout: "retry";
}
}
// Output: ["no user 7","retry"]Types in this example
function- function describeFailure(failure: NotFound | Timeout): string!!
describeFailure- function describeFailure(failure: NotFound | Timeout): stringPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<string, never, never>. failure- (parameter) failure: NotFound | Timeout
NotFound- (alias) class NotFound
Timeout- (alias) class Timeout
id- let id: string
Option, Exit, and Result (D208). The arms can run Effect functions.
const greet = (name: Option.Option<string>) =>
Option.match(name, { onNone: () => "Hello", onSome: (n) => `Hello, ${n}` })
const failed = (outcome: Exit.Exit<User, NotFound>) => Exit.isFailure(outcome)// run: Console.log(greet(Option.some("Ada"))); Console.log(greet(Option.none())); Console.log(failed(Exit.fail(new NotFound({ id: "7" })))); Console.log(failed(Exit.succeed({ id: "1", name: "Ada" })))
function greet(name: Option.Option<string>) {
return match (name) {
when Option.Some(let n): `Hello, ${n}`;
when Option.None: "Hello";
}
}
function failed(outcome: Exit.Exit<User, NotFound>) {
return outcome is Exit.Failure
}
// Output: Hello, Ada
// Output: Hello
// Output: true
// Output: falseTypes in this example
function- function greet(name: Option.Option<string>): string!!
greet- function greet(name: Option.Option<string>): stringPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<string, never, never>. name- (parameter) name: Option<string>
Option- (alias) module "effect/Option"
Option- type Option<A> = None<A> | Some<A>The
Optiondata type represents optional values. AnOption<A>is eitherSome<A>, containing a value of typeA, orNone, representing absence. n- let n: string
function- function failed(outcome: Exit.Exit<User, NotFound>): boolean!!
failed- function failed(outcome: Exit.Exit<User, NotFound>): booleanPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<boolean, never, never>. outcome- (parameter) outcome: Exit<User, NotFound>
Exit- (alias) module "effect/Exit"
Exit- type Exit<A, E = never> = Failure<A, E> | Success<A, E>Represents the result of an Effect computation.
User- (alias) interface User
NotFound- (alias) class NotFound
Test and bind (D211). Bindings in an is work in an if or while condition.
const firstFailure = (outcome: Exit.Exit<User, NotFound>) =>
Exit.isFailure(outcome) && Cause.hasFails(outcome.cause) ? Cause.pretty(outcome.cause) : "ok"// run: Console.log(firstFailure(Exit.succeed({ id: "1", name: "Ada" }))); Console.log(firstFailure(Exit.fail(new NotFound({ id: "7" }))).includes("NotFound"))
function firstFailure(outcome: Exit.Exit<User, NotFound>) {
if (outcome is Exit.Failure(let cause) && Cause.hasFails(cause)) return Cause.pretty(cause)
return "ok"
}
// Output: ok
// Output: trueTypes in this example
function- function firstFailure(outcome: Exit.Exit<User, NotFound>): string!!
firstFailure- function firstFailure(outcome: Exit.Exit<User, NotFound>): stringPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<string, never, never>. outcome- (parameter) outcome: Exit<User, NotFound>
Exit- (alias) module "effect/Exit"
Exit- type Exit<A, E = never> = Failure<A, E> | Success<A, E>Represents the result of an Effect computation.
User- (alias) interface User
NotFound- (alias) class NotFound
cause- let cause: Cause<NotFound>
Cause- (alias) module "effect/Cause"
hasFails- const hasFails: <NotFound>(self: Cause<NotFound>) => booleanReturns
trueif the cause contains at least oneFailreason. pretty- const pretty: <NotFound>(cause: Cause<NotFound>) => stringFormats a
Causeas a human-readable string for logging or debugging.
Escape hatches
Plain, self-contained TypeScript inside sync, try, and callback (D148, D229-D232). A throw in sync is a defect; in try it is an expected failure. Async bodies are Effect.promise and Effect.tryPromise. A hatch's function reads values of the .vibe file only as inputs, the arguments after it, which fill its first parameters; try's error mapper stays second (EK4405). runPromise leads back in from a hatch's body or a .ts file.
const id = Effect.sync(() => crypto.randomUUID())
const parsed = Effect.try({ try: () => JSON.parse(text), catch: (error) => new BadJson({ error }) })
const loaded = Effect.promise(() => Promise.resolve(42))
const fetched = Effect.tryPromise((signal) => fetch(url, { signal }))
const tick = Effect.callback<number>((resume) => {
const timer = setTimeout(() => resume(Effect.succeed(7)), 10)
return Effect.sync(() => clearTimeout(timer))
})
const name = await Effect.runPromise(greetUser("1"))// run: provide Logger.layer([sync(() => Logger.make(({ message }) => console.log(...(message as unknown[]))))]) { const value = hatches('{"ok":true}', "data:text/plain,hello"); Console.log(JSON.stringify({ ...value, id: /^[0-9a-f-]{36}$/.test(value.id) })) }
error BadJson { error: unknown }
function hatches(text: string, url: string) {
const id = sync(crypto.randomUUID())
const parsed = try((text) => JSON.parse(text), (error) => new BadJson({ error }), text)
const loaded = sync(async () => Promise.resolve(42))
const fetched = try(async (url, signal) => fetch(url, { signal }), url)
const tick = callback<number>((resume) => {
const timer = setTimeout(() => resume(7), 10)
return () => clearTimeout(timer)
})
const name = sync(async (greet) => runPromise(greet("1")), greetUser)
return { id, parsed, loaded, status: fetched.status, tick, name }
}
// Output: Hello, Ada
// Output: {"id":true,"parsed":{"ok":true},"loaded":42,"status":200,"tick":7,"name":"Ada"}Types in this example
error- class BadJson
BadJson- class BadJson
error- (property) error: unknown
hatches- function hatches(text: string, url: string): { id: `${string}-${string}-${string}-${string}-${string}`; parsed: any; loaded: number; status: number; tick: number; name: string; }!BadJson | UnknownError!
text- (parameter) text: string
url- (parameter) url: string
id- const id: `${string}-${string}-${string}-${string}-${string}`
sync- function sync<`${string}-${string}-${string}-${string}-${string}`>(value: `${string}-${string}-${string}-${string}-${string}`): `${string}-${string}-${string}-${string}-${string}`!!An escape hatch: its first argument is self-contained TypeScript (D229).Runs here and returns
${string}-${string}-${string}-${string}-${string}. crypto- var crypto: CryptoMDN Reference
randomUUID- (method) Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`The
randomUUID()method of the Crypto interface is used to generate a v4 UUID using a cryptographically secure random number generator. Available only in secure contexts. parsed- const parsed: any
try- (alias) function attempt<any, BadJson, [string]>(evaluate: (args_0: string) => any, mapError: (error: unknown) => BadJson, args_0: string): any!BadJson!An escape hatch: its first argument is self-contained TypeScript (D229). Inputs, which fill its first parameters:
text.Runs here and returnsany. JSON- var JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
parse- (method) JSON.parse(text: string, reviver?: (this: any, key: string, value: any) => any): anyConverts a JavaScript Object Notation (JSON) string into an object.
error- (parameter) error: unknown
BadJson- constructor BadJson<{ readonly error: unknown; }>(args: { readonly error: unknown; }): BadJson
loaded- const loaded: number
sync- function sync<number, []>(evaluate: (args_0: AbortSignal) => PromiseLike<number>): number!!An escape hatch: its first argument is self-contained TypeScript (D229).Runs here and returns
number. Promise- var Promise: PromiseConstructor
resolve- (method) PromiseConstructor.resolve<number>(value: number): Promise<number>Creates a new resolved promise for the provided value.
fetched- const fetched: Response
try- (alias) function attempt<Response, [string]>(evaluate: (args_0: string, args_1: AbortSignal) => PromiseLike<Response>, args_0: string): Response!UnknownError!An escape hatch: its first argument is self-contained TypeScript (D229). Inputs, which fill its first parameters:
url.Runs here and returnsResponse. signal- (parameter) signal: AbortSignal
fetch- function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>
signal- (property) RequestInit.signal?: AbortSignal | null | undefinedAn AbortSignal to set request's signal.
tick- const tick: number
callback- function callback<number, never, []>(register: (args_0: Resume<number, never>, args_1: AbortSignal) => void | (() => void)): number!!An escape hatch: its first argument is self-contained TypeScript (D229).Runs here and returns
number. resume- (parameter) resume: Resume<number, never>
timer- const timer: NodeJS.Timeout
setTimeout- function setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout namespace setTimeout
resume- (parameter) resume: (value: number) => voidSucceeds with value.
clearTimeout- function clearTimeout(timeout: NodeJS.Timeout | string | number | undefined): void
name- const name: string
sync- function sync<string, [(id: string) => string!NotFound!]>(evaluate: (args_0: (id: string) => string!NotFound!, args_1: AbortSignal) => PromiseLike<string>, args_0: (id: string) => string!NotFound!): string!!An escape hatch: its first argument is self-contained TypeScript (D229). Inputs, which fill its first parameters:
greetUser.Runs here and returnsstring. greet- (parameter) greet: (id: string) => string!NotFound!
runPromise- (alias) const runPromise: <string, NotFound>(effect: string!NotFound!, options?: RunOptions | undefined) => Promise<string>Executes an effect and returns the result as a
Promise. greetUser- (alias) function greetUser(id: string): string!NotFound!
id- (property) id: `${string}-${string}-${string}-${string}-${string}`
parsed- (property) parsed: any
loaded- (property) loaded: number
status- (property) status: number
status- (property) Response.status: numberThe
statusread-only property of the Response interface contains the HTTP status codes of the response. tick- (property) tick: number
name- (property) name: string
Fibers
Fork and join (D166, D225). A fiber belongs to the block that forks it. join [a, b] joins several and interrupts the rest on a failure.
const user = yield* Effect.forkChild(getUser("1"))
const orders = yield* Effect.forkChild(getOrders("1"))
return [yield* Fiber.join(user), yield* Fiber.join(orders)]// run: Console.log(JSON.stringify(both()))
function both() {
const user = fork getUser("1")
const orders = fork getOrders("1")
return [join user, join orders]
}
// Output: [{"id":"1","name":"Ada"},1]Types in this example
both- function both(): (number | User)[]!NotFound!
user- const user: Fiber<User, NotFound>
fork- const fork: <User!NotFound!>(effectOrOptions?: (User!NotFound!) | undefined, options?: {
readonly startImmediately?: boolean | undefined;
readonly uninterruptible?: boolean | "inherit" | undefined;
} | undefined) => Fiber<User, NotFound>!!ScopeRuns here and returns
Fiber<User, NotFound>.The prelude'sfork, for TypeScript callers and type positions. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
forkas a program: it runs when that program runs, not here, and returnsUser. orders- const orders: Fiber<number, never>
fork- const fork: <number!!>(effectOrOptions?: (number!!) | undefined, options?: {
readonly startImmediately?: boolean | undefined;
readonly uninterruptible?: boolean | "inherit" | undefined;
} | undefined) => Fiber<number, never>!!ScopeRuns here and returns
Fiber<number, never>.The prelude'sfork, for TypeScript callers and type positions. getOrders- function getOrders(id: string): numberPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<number, never, never>. join- joinJoins the fiber, or every fiber of the array or object, keeping the shape (Fiber.join), and returns
User. A failure interrupts the rest. Its failures propagate:NotFound. join- joinJoins the fiber, or every fiber of the array or object, keeping the shape (Fiber.join), and returns
number. A failure interrupts the rest.
Wait for several (D196). await.all behaves like Promise.all: it starts each element in its own fiber, keeps the shape, and a failure interrupts the rest.
const [user, orders] = yield* Effect.all([getUser("1"), getOrders("1")], { concurrency: "unbounded" })
const pair = yield* Effect.all({ user: getUser("1"), orders: getOrders("1") }, { concurrency: "unbounded" })// run: Console.log(JSON.stringify(many()))
function many() {
const [user, orders] = await.all [getUser("1"), getOrders("1")]
const pair = await.all { user: getUser("1"), orders: getOrders("1") }
return { user, orders, pair }
}
// Output: {"user":{"id":"1","name":"Ada"},"orders":1,"pair":{"user":{"id":"1","name":"Ada"},"orders":1}}Types in this example
many- function many(): { user: User; orders: number; pair: { user: User; orders: number; }; }!NotFound!
user- const user: User
orders- const orders: number
all- await.allStarts each program in its own fiber and waits for all of them, keeping the shape (Effect.all, unbounded). A failure interrupts the rest. Returns
[User, number]. Its failures propagate:NotFound. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
$awaitAllas a program: it runs when that program runs, not here, and returnsUser. getOrders- function getOrders(id: string): numberPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<number, never, never>. pair- const pair: { user: User; orders: number; }
all- await.allStarts each program in its own fiber and waits for all of them, keeping the shape (Effect.all, unbounded). A failure interrupts the rest. Returns
{ user: User; orders: number; }. Its failures propagate:NotFound. user- (property) user: User!NotFound!
orders- (property) orders: number!!
user- (property) user: User
orders- (property) orders: number
pair- (property) pair: { user: User; orders: number; }
First success, expected outcomes (D196, D258). await.any waits for the first success. await.allSettled returns successes and typed failures as Result values, rather than JavaScript's settlement objects, and never fails with a typed error. Defects and interruption propagate exactly as in Effect.all with mode: "result"; a cause with no typed failure still propagates. Wrap each element in exit(...) when every cause must become data.
const fastest = yield* Effect.raceAll([getUser("1"), getUser("2")])
const outcomes = yield* Effect.all([getUser("1"), getOrders("1")], { concurrency: "unbounded", mode: "result" })// run: const value = outcomes(); Console.log(value.fastest.name); Console.log(JSON.stringify(value.results.map((result) => result._tag))); Console.log(JSON.stringify((await.allSettled [getUser("missing"), getOrders("1")]).map((result) => result._tag))); Console.log(Exit.isFailure(exit(await.allSettled [die("defect"), getOrders("1")])))
function outcomes() {
const fastest = await.any [getUser("1"), getUser("2")]
const results = await.allSettled [getUser("1"), getOrders("1")]
return { fastest, results }
}
// Output: Ada
// Output: ["Success","Success"]
// Output: ["Failure","Success"]
// Output: trueTypes in this example
outcomes- function outcomes(): { fastest: User; results: [Result<User, NotFound>, Result<number, never>]; }!NotFound!
fastest- const fastest: User
getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
$awaitAnyas a program: it runs when that program runs, not here, and returnsUser. results- const results: [Result<User, NotFound>, Result<number, never>]
allSettled- await.allSettledStarts each program in its own fiber and waits for all of them, returning each outcome as a Result (Effect.all with mode "result"). It never fails. Returns
[Result<User, NotFound>, Result<number, never>]. getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
$awaitAllSettledas a program: it runs when that program runs, not here, and returnsUser. getOrders- function getOrders(id: string): numberPlain JavaScript: VibeLang code calls it directly. TypeScript callers see
Effect<number, never, never>. fastest- (property) fastest: User
results- (property) results: [Result<User, NotFound>, Result<number, never>]
Fork a block (D166). The loop stops when the enclosing block exits.
const withHeartbeat = Effect.scoped(Effect.gen(function* () {
yield* Effect.forkScoped(Effect.forever(Effect.andThen(Effect.log("tick"), Effect.sleep("1 second"))))
return yield* getUser("1")
}))// run: provide Logger.layer([]) { Console.log(withHeartbeat().name) }
function withHeartbeat() {
fork {
while (true) {
log("tick")
sleep("1 second")
}
}
return getUser("1")
}
// Output: AdaTypes in this example
withHeartbeat- function withHeartbeat(): User!NotFound!
fork- const fork: <!!>(effectOrOptions?: (!!) | undefined, options?: {
readonly startImmediately?: boolean | undefined;
readonly uninterruptible?: boolean | "inherit" | undefined;
} | undefined) => Fiber<never, never>!!ScopeRuns here and returns
Fiber<never, never>.The prelude'sfork, for TypeScript callers and type positions. log- const log: (...message: ReadonlyArray<any>) => void!!Runs here and returns
void.Logs one or more messages using the default log level. sleep- const sleep: (duration: Duration.Input) => void!!Runs here and returns
void.Returns an effect that suspends the current fiber for the specified duration without blocking a JavaScript thread. getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User.
Detached and scoped fibers (D170).
yield* Effect.forkDetach(getUser("1"))
const scope = yield* Scope.make()
const fiber = yield* Effect.forkIn(getUser("2"), scope)// run: Console.log(Exit.isFailure(Fiber.await(background())))
function background() {
detach(getUser("1"))
const scope = Scope.make()
const fiber = forkIn(scope, getUser("2"))
Scope.close(scope, Exit.void)
return fiber
}
// Output: trueTypes in this example
background- function background(): Fiber<User, NotFound>!!
detach- const detach: <User, NotFound, never>(program: User!NotFound!) => Fiber<User, NotFound>!!Runs here and returns
Fiber<User, NotFound>.D170: starts a program in a fiber that nothing owns (Effect.forkDetach). getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
detachas a program: it runs when that program runs, not here, and returnsUser. scope- const scope: Closeable
Scope- (alias) module "effect/Scope"
make- const make: (finalizerStrategy?: "sequential" | "parallel") => Closeable!!Runs here and returns
Closeable.Creates a newScopewith the specified finalizer strategy. fiber- const fiber: Fiber<User, NotFound>
forkIn- const forkIn: <User, NotFound, never>(scope: Scope, program: User!NotFound!) => Fiber<User, NotFound>!!Runs here and returns
Fiber<User, NotFound>.D170: starts a program in a fiber thatscopeowns (Effect.forkIn). getUser- (alias) function getUser(id: string): User!NotFound!Passed by name to
forkInas a program: it runs when that program runs, not here, and returnsUser. close- const close: <void, never>(self: Closeable, exit: Exit<void, never>) => void!!Runs here and returns
void.Closes a scope and runs its registered finalizers. Exit- (alias) module "effect/Exit"
void- (alias) const void_: Exit<void, never>
Wait for a Deferred (D227). await waits for promises in JavaScript, so a Deferred uses its method.
const ready = yield* Deferred.make<string>()
yield* Deferred.succeed(ready, "go")
const value = yield* Deferred.await(ready)// run: Console.log(signal())
function signal() {
const ready = Deferred.make<string>()
Deferred.succeed(ready, "go")
return ready.await()
}
// Output: goTypes in this example
signal- function signal(): string!!
ready- const ready: Deferred<string, never>
Deferred- (alias) module "effect/Deferred"
make- const make: <string, never>() => Deferred<string, never>!!Runs here and returns
Deferred<string, never>.Creates a newDeferred. succeed- const succeed: <string, never>(self: Deferred<string, never>, value: string) => boolean!!Runs here and returns
boolean.Attempts to complete theDeferredwith the specified value. await- (property) await: <string, never>(self: Deferred<string, never>) => string!!Method syntax: calls
Deferred.await(ready, …).Runs here and returnsstring.
Race (D196, D197). await.race behaves like Promise.race: the first to finish wins, success or failure, and the rest are interrupted. It replaced select (EK4607); poll with Queue.poll and an if.
const next = yield* Effect.raceFirst(
Effect.map(Queue.take(jobs), (job) => `ran ${job}`),
Effect.as(Effect.sleep("1 second"), "timeout")
)// run: Console.log(next())
function next() {
const jobs = Queue.unbounded<string>()
Queue.offer(jobs, "build")
return await.race [
`ran ${Queue.take(jobs)}`,
do {
sleep("1 second")
"timeout"
}
]
}
// Output: ran buildTypes in this example
next- function next(): string!!
jobs- const jobs: Queue<string, never>
Queue- (alias) module "effect/Queue"
unbounded- const unbounded: <string, never>() => Queue<string, never>!!Runs here and returns
Queue<string, never>.Creates an unbounded queue that can grow to any size without blocking producers. offer- const offer: <string, never>(self: Enqueue<string, never>, message: string) => boolean!!Runs here and returns
boolean.Adds a message to the queue. Returnsfalseif the queue is done. race- await.raceStarts each program in its own fiber; the first to finish wins, success or failure, and the others are interrupted (Effect.raceFirst). Returns
string. take- const take: <string, never>(self: Dequeue<string, never>) => string!!Passed by name to
$awaitRaceas a program: it runs when that program runs, not here, and returnsstring.Takes a single message from the queue, or wait for a message to be available. sleep- const sleep: (duration: Duration.Input) => void!!Runs here and returns
void.Returns an effect that suspends the current fiber for the specified duration without blocking a JavaScript thread.
A concurrent loop (D169). At most n items run at once, and all finish before the loop ends.
yield* Effect.forEach(ids, (id) => Effect.flatMap(getUser(id), (user) => Effect.log(user.name)), {
concurrency: 8,
discard: true
})// run: Effect.provide(logAll(["1", "2"]), Logger.layer([sync(() => Logger.make(({ message }) => console.log(...(message as unknown[]))))]))
function logAll(ids: string[]) {
for concurrent(8) (const id of ids) log(getUser(id).name)
}
// Output: Ada
// Output: AdaTypes in this example
logAll- function logAll(ids: string[]): void!NotFound!
ids- (parameter) ids: string[]
concurrent- for concurrentRuns the body for each item in its own fiber and joins them all before the loop ends; a failure interrupts the rest.
id- (parameter) id: string
log- const log: (...message: ReadonlyArray<any>) => void!!Runs here and returns
void.Logs one or more messages using the default log level. getUser- (alias) function getUser(id: string): User!NotFound!Runs here and returns
User. name- (property) User.name: string