Sequential File Processing
Process a list of items one at a time, accumulating results — the workflow equivalent of Array.reduce() or a functional fold.
Workflow
runPipeline(
pipe(
readFileList,
initLoopState,
loop<LoopResult, LoopState>((recur, done) =>
pipe(
processItem,
advanceOrFinish,
).branch({
Continue: recur,
Done: done,
})
),
printFinalResults,
),
);
This is a fold
If you've written reduce or fold, you already know this pattern:
// JavaScript reduce:
files.reduce((results, file) => [...results, process(file)], [])
// Barnum loop — same structure, different encoding:
// - init: readFileList → initLoopState (seed the accumulator)
// - step: processItem (apply f to current element + accumulator)
// - advance: advanceOrFinish (pop next element or return accumulator)
The loop combinator is Barnum's encoding of a left fold. The LoopState carries both the "remaining items" and the "accumulated results" — exactly like the accumulator in reduce. Each iteration processes one item, appends to results, and either recurs with the tail or breaks with the final value.
The key difference from reduce: each iteration is a full pipeline step. The "function" applied at each step can be an LLM call, a subprocess, a network request — anything a handler can do. The fold structure gives you sequential guarantees (each item sees the results of all prior items) without sacrificing the ability to do real work at each step.
Stages
- Read file list — load the list of items to process.
- Init loop state — seed the accumulator:
{ current, rest, results: [] }. - Process item — run the operation on
current, append toresults. - Advance or finish — if
restis non-empty, shift the next item intocurrentandContinue. OtherwiseDonewith the final results. - Print results — consume the completed accumulator.
Key points
- Sequential by construction: unlike
.iterate().map()which fans out in parallel,loopprocesses one item at a time. Use this when order matters or when each step depends on prior results. - Accumulator pattern:
LoopStatecarries both the work queue and the running result. This is the standard left-fold shape — seed, step, done. - Tagged union for termination:
advanceOrFinishreturnsContinueorDone, whichbranchroutes torecurordone. No boolean flags, no sentinel values — the type system enforces that every iteration either continues or terminates. - When to use parallel instead: if items are independent (no item needs prior results), use
.iterate().map()from the codebase migration pattern. Use this sequential fold when results accumulate or ordering constraints exist.