Skip to content

Let operators inside a control block read loop variables through $var property references #8635

Description

@aglinxinyuan

Feature Summary

Inside a control block (LoopStart/LoopEnd), the iteration state — the loop's variables such as i — reaches every operator as a state message, but no built-in operator can use it. An operator's properties are read once when its executor is initialized, before any message has arrived, and the executor is built from a frozen descriptor JSON with no substitution stage. Today the only operator that reads a state key is If; a Filter inside a loop cannot take its threshold from the loop.

Let a property of any operator inside a block refer to a loop variable as $i, with no change to the operator's source.

Proposed Solution or Design

  1. Parse phase (unchanged wire format). The frontend sends $i as the property value. When $i meets a numeric or boolean property, the parser puts in a typed placeholder so the descriptor still deserializes, and records the reference on the operator (stateReferences: JSON pointer → variable name). String properties keep the literal. The sidecar is hidden from the property-panel schema.
  2. Compile phase. The compiler decides which operators lie inside a loop block. Inside a block, the references are kept, including whole-string $name literals. Outside every block, a string such as $AAPL stays a literal, and a reference in a numeric or boolean property is a compile error.
  3. Late binding. An executor whose descriptor carries stateReferences is created as a late-bound wrapper. It forwards the state messages, binds the placeholders at the first tuple from their merged values (patching the descriptor and instantiating the real executor), and reuses the bound values for every tuple of the iteration. A tuple that arrives while a referenced variable is still missing is an error naming it.

Registering the state on Python operators (state) is #8641. Out of scope here: operators whose Scala descriptor interpolates property values into generated Python code (the sklearn operators); their values never reach a Scala executor, so Python-side binding is a follow-up. UDF-side sugar and the frontend are separate issues (#8636).

Acceptance

  • A Filter inside a loop with predicate value $i filters with the current iteration's i, verified end to end in LoopIntegrationSpec.
  • Unit tests for the plan rewriter (string, integer and nested-pointer references) and for the late-bound executor (bind at the first state, reuse, error when unbound / unknown name).
  • No per-operator code changes.

Affected Area

Workflow Engine (Amber)

Activity

  1. added 4 commits that reference this issue on Sep 22, 2026
    ccf1079
    b254594
    ce118d3
    0567211
  2. added 4 commits that reference this issue on Sep 23, 2026
    ffdef62
    97de6cc
    08921b2
    54ec7dc
  3. added a commit that references this issue on Oct 3, 2026
    d7ce3a6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions