Producers
A producer yields values one at a time and can run Effects between yields. Use function* to declare one and for...of to consume it.
Yield values on demand
// run: Console.log(firstAlert())
function* readings() {
try {
for (const celsius of [18, 21, 25, 19, 28, 30]) {
yield celsius
}
} finally {
Console.log("sensor closed")
}
}
function firstAlert() {
for (const celsius of readings()) {
if (celsius >= 25) return celsius
}
return undefined
}
// number | undefined!!
// Output: sensor closed
// Output: 25Types in this example
readings- function readings(): Stream<number, never, never>
celsius- const celsius: number
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. firstAlert- function firstAlert(): number | undefined!!
undefined- (property) undefined: undefined
Calling readings() constructs a lazy source. Its body starts when a consumer asks for a value. Each yield hands one value to the loop, and the producer waits until the consumer continues.
Here the consumer returns at 25. Before firstAlert returns, the producer's finally logs sensor closed. break, a return, or a failure in the loop body closes the source and runs its cleanup. Iterating the source again starts a new run.
In .vibe, function* declares a producer. Do not use it to write Effect.gen or other generator-based APIs. Generator methods on objects remain JavaScript generators.
Call services between yields
// run: provide NodeServices.layer { const fs = Effect.service(FileSystem); using dir = fs.makeTempDirectoryScoped(); fs.writeFileString(`${dir}/a.txt`, "short"); fs.writeFileString(`${dir}/b.txt`, "x".repeat(1001)); Console.log(firstLargeFile(dir) === `${dir}/b.txt`) }
import { FileSystem } from "effect/FileSystem"
import { Path } from "effect/Path"
import { NodeServices } from "@effect/platform-node"
function* fileTexts(directory: string) {
const names = FileSystem.readDirectory(directory).sort()
for (const name of names) {
const path = Path.join(directory, name)
const info = FileSystem.stat(path)
if (info.type === "File") {
yield { path, text: FileSystem.readFileString(path) }
}
}
}
function firstLargeFile(directory: string) {
for (const file of fileTexts(directory)) {
if (file.text.length > 1000) return file.path
}
return undefined
}
// Output: trueTypes in this example
FileSystem- (alias) const FileSystem: Service<FileSystem, FileSystem> (alias) interface FileSystemService tag for platform file-system operations.
Path- (alias) const Path: Service<Path, Path>
(alias) interface Path
(alias) namespace PathService tag for accessing the current
Pathimplementation. NodeServices- (alias) module "@effect/platform-node/NodeServices"
fileTexts- function fileTexts(directory: string): Stream<{ path: string; text: string; }, PlatformError, FileSystem | Path>
directory- (parameter) directory: string
names- const names: string[]
FileSystem- (alias) const FileSystem: Service<FileSystem, FileSystem>Service tag for platform file-system operations.
readDirectory- (property) FileSystem.readDirectory: (path: string, options?: {
readonly recursive?: boolean | undefined;
}) => Array<string>!PlatformError!Runs here and returns
string[].List the contents of a directory. sort- (method) Array<string>.sort(compareFn?: ((a: string, b: string) => number) | undefined): string[]Sorts an array in place. This method mutates the array and returns a reference to the same array.
name- const name: string
path- const path: string
Path- (alias) const Path: Service<Path, Path>Service tag for accessing the current
Pathimplementation. join- (property) Path.join: (...paths: ReadonlyArray<string>) => string
info- const info: File.Info
stat- (property) FileSystem.stat: (path: string) => File.Info!PlatformError!Runs here and returns
File.Info.Get information about a file atpath. SeeFile.Infofor metadata limits. type- (property) File.Info.type: File.Type
path- (property) path: string
text- (property) text: string
readFileString- (property) FileSystem.readFileString: (path: string, encoding?: string) => string!PlatformError!Runs here and returns
string.Read the contents of a file. firstLargeFile- function firstLargeFile(directory: string): string | undefined!PlatformError!FileSystem | Path
file- const file: { path: string; text: string; }
length- (property) String.length: numberReturns the length of a String object.
undefined- (property) undefined: undefined
Service calls run as they do in an ordinary function. The producer's failures and requirements join the consuming function's contract. Provide NodeServices.layer at the application boundary to supply FileSystem and Path, as in examples/file-tree.
A producer can acquire resources with using. They stay open between yields and close when consumption finishes or stops early. This example reads one whole file at a time; stopping avoids reading the remaining files.
Use a producer as a Stream
// run: Console.log(JSON.stringify(alerts()))
function alerts() {
const selected = readings().filter((value) => value >= 25).map((value) => `${value} C`).take(2)
const result: string[] = []
for (const alert of selected) result.push(alert)
return result
// ["25 C", "28 C"]; sensor closed
}
// Output: sensor closed
// Output: ["25 C","28 C"]Types in this example
alerts- function alerts(): string[]!!
selected- const selected: Stream<string, never, never>
readings- (alias) function readings(): Stream<number, never, never>
filter- (property) filter: <number, never, never>(self: Stream<number, never, never>, predicate: Predicate<number>) => Stream<number, never, never>Method syntax: calls
Stream.filter(readings(), …).Filters a stream to the elements that satisfy a predicate. value- (parameter) value: number
map- (property) map: <number, never, never, string>(self: Stream<number, never, never>, f: (a: number, i: number) => string) => Stream<string, never, never>Method syntax: calls
Stream.map(readings().filter((value) => value >= 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(readings().filter((value) => value >= 25).map((value) =>${value} C), …).Takes the firstnelements from this stream. result- const result: string[]
alert- const alert: string
push- (method) Array<string>.push(...items: string[]): numberAppends new elements to the end of an array, and returns the new length of the array.
A producer has type Stream.Stream<Value, Error, Requirements>. Stream operators accept it directly, and TypeScript consumers see a Stream in its declaration file. The examples/pipeline program uses this filter, map, and take pattern.
When Stream operators consume a producer, the runtime starts it on its own fiber and uses a handoff with no buffer. The producer yields only when the Stream pulls; closing the Stream interrupts the producer and runs cleanup. Handle control operations inside that producer before sending it to Stream operators.
Iterate any Stream
// run: Console.log(sumStream())
function sumStream() {
let total = 0
for (const value of Stream.fromIterable([1, 2, 3])) {
total += value
}
return total
}
// Output: 6Types in this example
sumStream- function sumStream(): number!!
total- let total: number
value- const value: number
Stream- (alias) module "effect/Stream"
fromIterable- const fromIterable: <number>(iterable: Iterable<number>, options?: {
readonly chunkSize?: number | undefined;
}) => Stream<number, never, never>Creates a new
Streamfrom an iterable collection of values.
for...of pulls an Effect Stream and closes it when the loop exits, including on break. Arrays, maps, sets, and strings keep JavaScript's ordinary iteration behavior.
Use for (const value of source) for producers. This preview does not support for (using value of source) over a producer, and Array.from(source) is not a Stream consumer. Use Stream.runCollect(source) when you need all the elements as an array.
Coming from Effect
A generator wrapped in Stream.unwrap becomes a function*. Write calls between yield statements; reserve yield for the values the producer emits. Use Stream's existing operators whenever they express the transformation you need.