Skip to content

reduce: name the shared key container to fix HRTB normalization - #797

Merged
frankmcsherry merged 1 commit into
TimelyDataflow:master-nextfrom
frankmcsherry:reduce-named-key-container
Jul 14, 2026
Merged

reduce: name the shared key container to fix HRTB normalization#797
frankmcsherry merged 1 commit into
TimelyDataflow:master-nextfrom
frankmcsherry:reduce-named-key-container

Conversation

@frankmcsherry

Copy link
Copy Markdown
Member

reduce_abelian / reduce_core / reduce_trace equated the output cursor's key to the input cursor's key projection
(for<'a> Key<'a> = BatchKey<'a, Tr1>). That higher-ranked projection-to-projection bound fails to normalize when both trace types are generic (a reduce operator generic over both traces) and keyed by a non-reference GAT such as a columnar Ref -- the shape the Materialize master-next port hits. The failure is error[E0271]: expected <_ as Cursor>::Key, found <the key> at the wrapper's call site.

Introduce a named key-container parameter KC: the input pins it via KeyContainer = KC (uniquely inferable, since the input is concrete at a call site), and both cursor keys are equated to KC::ReadItem<'a>, leaving projection-to-named-type, which the solver does normalize. The explicit Key<'a> = KC::ReadItem<'a> bound on the input is stated because the solver does not derive it from KeyContainer = KC under for<'a>, and the internal CursorTactic needs it to equate the two cursors' keys. Key-type semantics are unchanged. This is the same technique already used for Diff = R1/R2 in join_core.

A wrapper generic over both traces (the mz_reduce_abelian shape) satisfies these bounds by forwarding KC and pinning KeyContainer = KC on both of its own traces, so KC stays inferable from the named output spine at its call sites.

`reduce_abelian` / `reduce_core` / `reduce_trace` equated the output cursor's
key to the *input* cursor's key projection
(`for<'a> Key<'a> = BatchKey<'a, Tr1>`). That higher-ranked
projection-to-projection bound fails to normalize when both trace types are
generic (a reduce operator generic over both traces) and keyed by a
non-reference GAT such as a columnar `Ref` -- the shape the Materialize
master-next port hits. The failure is `error[E0271]: expected
<_ as Cursor>::Key, found <the key>` at the wrapper's call site.

Introduce a named key-container parameter `KC`: the input pins it via
`KeyContainer = KC` (uniquely inferable, since the input is concrete at a
call site), and both cursor keys are equated to `KC::ReadItem<'a>`, leaving
projection-to-named-type, which the solver does normalize. The explicit
`Key<'a> = KC::ReadItem<'a>` bound on the input is stated because the solver
does not derive it from `KeyContainer = KC` under `for<'a>`, and the internal
`CursorTactic` needs it to equate the two cursors' keys. Key-type semantics
are unchanged. This is the same technique already used for `Diff = R1/R2` in
`join_core`.

A wrapper generic over both traces (the `mz_reduce_abelian` shape) satisfies
these bounds by forwarding `KC` and pinning `KeyContainer = KC` on both of its
own traces, so `KC` stays inferable from the named output spine at its call
sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@frankmcsherry
frankmcsherry force-pushed the reduce-named-key-container branch from 26f11da to fc64c78 Compare July 13, 2026 23:49
@frankmcsherry
frankmcsherry merged commit 1298220 into TimelyDataflow:master-next Jul 14, 2026
6 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant