Skip to content

Unnest: decompose inside LAMBDA bodies via a scoped nested LET #279

Description

@jimmytacks

Problem

After #278 (opaque LAMBDA/LET), /Unnest correctly leaves inner LAMBDA(...) bodies intact — but it no longer decomposes them at all. For formulas built around inner lambdas, the gnarliest nesting is exactly the part left as one blob.

Example

=ROUND(MIN(BYROW(HSTACK(...), LAMBDA(r, SUM(PAIROP(r, LAMBDA(a,b, SQRT(SUMSQ(PAIROP(XV(VSTACK(a,b), t[[City]:[Y-Coordinates]], {2,3}),,1)))*100),,1)))))+45,)

The distance calc SQRT(SUMSQ(PAIROP(XV(VSTACK(a,b), …)))) lives inside LAMBDA(a, b, …) and is the most opaque, most debug-worthy part — yet #278 leaves it inline because it references the bound params a/b.

Proposed solution (Option 3 — nested-LET-in-LAMBDA)

Decompose inside lambda bodies too, emitting a LET(...) inside the LAMBDA body to hold the steps that reference the lambda's parameters, so scope is preserved:

lambda1, LAMBDA(a, b, LET(
    vstack1, VSTACK(a, b),
    xv1,     XV(vstack1, t[[City]:[Y-Coordinates]], {2,3}),
    pairop1, PAIROP(xv1, , 1),
    …,
    body)),

This is the only option that exposes the inner calc as named, inspectable steps while keeping the formula valid.

Design notes / open questions for the spec

  • Synthesiser must emit nested LETs and place each step in the correct (innermost-binding) scope.
  • Scope analysis — a step belongs to the innermost lambda scope that binds any of its free variables; closed sub-expressions can still hoist to an outer scope (this subsumes the Option 2 "scope-aware hoisting" idea).
  • Dialog must present steps grouped by scope (a renamed / un-included step belongs to a specific lambda's LET), and naming/uniqueness becomes per-scope.
  • Preview + round-trip need to handle the nested-LET shape; /Refactor and /LET to LAMBDA composition over a lambda-with-inner-LET should still work.

Needs a spec (0010) and Tim's approval before planning. Depends on #278.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions