Skip to content

Refactor Range/RaggedRange argument resolution into a two-phase design #552

@khatchad

Description

@khatchad

Motivation

Range.getShapes and RaggedRange.getShapes resolve argument value-numbers through a per-positional-count branch structure (numPosArgs == 0/1/2/3), with each branch handling both:

  1. Raw arg resolution: walking positional + keyword args to get each formal's value-number.
  2. TF convention application: the startlimit swap when only one of them was given (TF treats range(5) as limit=5, and range(starts=5) as limits=5 since limits is the mandatory parameter).

These two concerns are intertwined per-branch, which makes the logic hard to reason about. The bug fixed incidentally in ponder-lab/ML#332 was a direct symptom: the keyword-only swap range(starts=5)limits=5 was firing BEFORE the keyword-fallback block, which then re-fetched starts and undid the swap. The fix moved the swap to fire AFTER the fallback. Pre-fix this was invisible only because both pre- and post-swap states landed at (1, RaggedDim); once NumericDim specialisation was added, the bug surfaced.

Proposed Refactor

A two-phase design:

// Phase 1: resolve raw arg name → value-number.
ArgResolution args = resolveArgs(builder, numPosArgs);
// args.starts, args.limits, args.deltas are either VN or UNDEFINED.

// Phase 2: apply TF's `tf.range`/`tf.ragged.range` argument-swap conventions.
applyRangeConventions(args);

// Phase 3: compute shape from resolved + convention-applied args.
return computeShape(args);

Phase 1 walks the invoke's positional + keyword params once and produces a clean record (e.g., a record RangeArgs(int startsVn, int limitsVn, int deltasVn)). Phase 2 applies the TF semantic quirks in a single place. Phase 3 is the actual shape computation.

Benefits:

  • No branching on numPosArgs for arg resolution—positional and keyword treated uniformly.
  • TF conventions documented in one place, not scattered across if/else branches.
  • Easier to extend (e.g., adding tf.range variants or new ragged constructors) without expanding the branch matrix.
  • Eliminates the class of bug fixed in ponder-lab/ML#332.

Scope

  • Range.java (lines 71-220 approx)—the main per-positional-count branching plus the keyword-fallback re-fetch block at the end.
  • RaggedRange.java—inherits the same structural shape from its parent, so the refactor should be applied to both together.

Out of Scope

  • Other generators with positional/keyword arg handling that don't use the range-style start/limit swap. They follow different conventions and the refactor here may or may not apply (each generator's API quirks are different).
  • The tf.range vs tf.ragged.range divergence in shape computation (1D vs 2D ragged)—Phase 3 still differs between subclasses; the refactor only unifies Phases 1-2.

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions