Warning: Effeck is v0. The language will change in major, breaking ways. It is not suitable for production use.

Sink Operations

Transform a Sink when its existing consumption rule fits but its input or result type does not. Pure mappings build a new sink; effectful mappings defer their work until consumption. Filter the feeding Stream when only selected elements should reach a sink.

Adapting consumer input

const quantities = Sink.sum.mapInput((entry: { quantity: number }) => entry.quantity)

function countItems() {
  return Stream.make({ quantity: 7 }, { quantity: 11 }).run(quantities)
}
// Returns 18.

mapInput converts each incoming value before the original sink sees it. Its callback is synchronous and cannot perform Effect work. Use mapInputEffect for validation, service calls, or other work with failures and requirements.

error InvalidQuantity {
  raw: string
}

const parsedTotal = Sink.sum.mapInputEffect((raw: string) => {
  const value = Number(raw)
  if (!Number.isFinite(value)) throw new InvalidQuantity({ raw })
  return value
})

The new sink accepts strings and can fail with InvalidQuantity. A failed input transformation fails consumption; it does not silently discard the input.

Mapping consumer output

const report = Sink.sum.map((total) => ({ total, unit: "items" }))

function totalReport() {
  return Stream.make(7, 11).run(report)
}
// Returns { total: 18, unit: "items" }.

map runs once on the sink's successful final result. It leaves the input and leftover types unchanged. mapEffect allows Effect work when transforming that result, such as saving a summary.

Composing input and output transformations

const report = Sink.sum
  .mapInput((entry: { bytes: number }) => entry.bytes)
  .map((bytes) => `${bytes} bytes`)

function byteReport() {
  return Stream.make({ bytes: 128 }, { bytes: 256 }).run(report)
}
// Returns "384 bytes".

The input stage adapts each element, the original sink accumulates, and the output stage formats once. The method chain builds a Sink value. Stream.run is the operation that executes it.

Filtering before consumption

function positiveBatches() {
  const positive = Stream.make(-3, 4, 0, 8, -1, 12, 16).filter((n) => n > 0)
  const batches = positive.transduce(Sink.take<number>(3))
  return batches.runCollect() // [[4, 8, 12], [16]]
}

Filtering upstream means rejected elements do not count toward the sink's limit. Stream.transduce repeatedly runs the sink and passes batch leftovers into the next run. If input ends exactly at a completed sink boundary, a final empty sink result can appear; remove empty arrays downstream when the application needs only non-empty groups.