Pipelines: byte processes and value folds
| is dataflow: it threads each stage’s output into the next, and what it does
at runtime is decided by the type of the connecting edge, not by any keyword.
The byte modes of computation types select one of two execution
models.
Failure is a separate axis. A pipeline propagates a stage’s failure, but the
pipe never reacts to it: recovering from failure is ?’s and try’s job, and
branching is on Bool, never on command success (failure).
Value pipelines are folds. When every stage operates on the value channel,
x | f is typed data-last composition — it reduces to f !{x} and is evaluated
sequentially in the parent. No process is spawned, no pipe exists, no process
group is formed. range 1 21 | filter $even | sum is three function calls
threaded by the value channel.
Byte pipelines are processes. As soon as one edge touches bytes — an external command, or any byte-output stage — the whole pipeline runs as a Unix-style process pipeline:
- every stage, including ral-implemented ones, executes in a subprocess;
- all subprocesses share one process group;
- the parent ral process is not a member of that group.
This is what lets a terminal-touching pipeline behave exactly as a shell’s does,
regardless of whether a stage is /bin/cat, a handler, or a ral block.
Out-of-process stages are therefore subshells with respect to mutation: a
helper stage’s cd, env, alias, or module changes do not flow back to the
parent — only the pipe contents and the final value cross the boundary. This
matches every traditional shell and is what keeps job control coherent: a shell
process inside its own foreground pipeline cannot consistently both own the
terminal and not own it. It is the same isolation a spawned block enjoys
(immutable bindings).
The terminal-handoff and process-containment machinery is transport detail, not
semantics. Unix uses process groups, a foreground guard, and helper job-frame
gates where a tty handoff must settle before user code runs; Windows has no
tcsetpgrp analogue, so it uses Job Objects after spawn and still needs a custom
creation-time launch path to close its handle-inheritance and early-fork windows.
The moving parts live in the runtime’s pipeline/ and
process maps.
See also types, cbpv, scoping.
Realised in pipeline-execution.
Cite: RATIONALE §“Byte pipelines are processes;
value pipelines are folds”, §“Piping and failure”, §“No command-level ||”,
§“Concurrency: isolation, not shared state”; docs/SPEC.md §4, §13, §20.4.