# zip

Zips two Fx streams in strict lockstep: emits a pair `[a, b]` only when both
streams have produced their next value. Emits the i-th pair when both have
produced at least i values.

**Completion:** The zipped stream completes when the **first** of the two
streams completes (no further pairs are emitted). The other stream is
interrupted.

**Errors:** The first failure from either stream fails the zipped stream.

## Signatures

```ts
export declare const zip: {
    <A2, E2, R2>(that: Fx<A2, E2, R2>): <A, E, R>(self: Fx<A, E, R>) => Fx<readonly [
        A,
        A2
    ], E | E2, R | R2>;
    <A, E, R, A2, E2, R2>(self: Fx<A, E, R>, that: Fx<A2, E2, R2>): Fx<readonly [
        A,
        A2
    ], E | E2, R | R2>;
};
```

## Why

`zip` preserves one-to-one positional correspondence between two producers. Values wait in
unbounded queues until their counterpart arrives, and pairs emit in each source's order.

## Ownership and lifetime

Both child runs and queues belong to the consumer. The first completion, any failure, or
interruption stops both fibers and discards unmatched queued values.

## Examples

```ts
import { Fx } from "@typed/fx"
const pairs = Fx.zip(Fx.fromIterable([1, 2]), Fx.fromIterable(["a", "b"]))
```
