From f9dd2e8149d62eba1a39ffa0be77a6dd2a6e29c4 Mon Sep 17 00:00:00 2001 From: Tim Jacks <53003551+jimmytacks@users.noreply.github.com> Date: Thu, 14 May 2026 21:10:24 +0100 Subject: [PATCH] Document array-lifting dispatcher pattern in create-lambda skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the audit in #179, codify the pattern so future LAMBDAs lift by default when their shape fits. New "Array-lifting Support" section covers: - When to apply (scalar input + scalar output → lift; array input or array output → skip; constant helpers → skip) - The dispatcher template (inline scalar LAMBDA + MAP/scalar branch on ROWS/COLUMNS > 1, placed after the Help? branch) - Picking the lift parameter (default to primary scalar arg) - Help text and test conventions - Pointers to existing examples (maps/, NTH, CHARQ, REVERSESTRING, TLOOKUP, plus CONTAINS/CIRCPOS as naturally-lifting cases) Also add a bullet to Step 2 (Confirm Details) so the "should this lift?" question surfaces during planning instead of being a post-hoc audit. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/create-lambda/SKILL.md | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/.claude/skills/create-lambda/SKILL.md b/.claude/skills/create-lambda/SKILL.md index c45a270..1e6e6fb 100644 --- a/.claude/skills/create-lambda/SKILL.md +++ b/.claude/skills/create-lambda/SKILL.md @@ -126,6 +126,73 @@ The `.lambda` file MUST use this exact format. Pay close attention to spacing, l - Closing `)`: 0 spaces - `);`: 0 spaces +## Array-lifting Support + +Whenever a new LAMBDA has **at least one scalar-shaped input and a scalar-shaped output**, design it to lift per-parameter — i.e. passing an array of values to the scalar param returns an element-wise array of results. Without lifting, callers who point a column at the lambda silently get a single mixed-up value (the body's aggregators collapse the array). This is a silent footgun, not an error, so it's easy to miss in review. + +### When to apply + +| Input shape | Output shape | Lift? | +|---|---|---| +| ≥1 scalar param | scalar | ✅ Yes — apply dispatcher | +| All array-shaped params (e.g. lookup tables, grids) | scalar or array | ➖ No — inputs are arrays by design | +| Any | inherently array (region/window extractors, generators) | ➖ No — output is already array | +| No real input (constant-table helpers) | array | ➖ No — nothing to lift | + +If you're unsure, ask: "does the scalar param semantically take **one** value?" If yes, lift it. + +### Dispatcher pattern + +Keep the scalar body inside an inline `scalar` LAMBDA within the `LET`, then branch on whether the lift param is multi-cell: + +``` +// Procedure + scalar, LAMBDA(, + LET(... scalar body ..., + )), + result, IF(OR(ROWS() > 1, COLUMNS() > 1), + MAP(, scalar), + scalar()), + IF(Help?, Help, result) +``` + +The dispatcher MUST sit **after** the `Help?, ISOMITTED(...)` line so `=YourLambda()` still returns help text. Scalar callers see no behavioural change. + +### Picking the lift parameter + +When multiple parameters could lift, default to lifting the **primary** scalar arg (the one most callers vary). Alternatives, only if the use case clearly demands it: + +- **Cartesian product** — more powerful, more complex; needs justification. +- **Equal-shape zip** — when two arrays of the same shape should be paired element-wise. + +Most LAMBDAs just lift the primary arg. Document the choice in the help text. + +### Help text + +Add an "Also accepts an array of " note to the lifted parameter's description, e.g.: + +``` +" n →(Required) 1-based position into the flattened array; negative counts from the end (-1 = last). Also accepts an array of indices — result lifts element-wise.¶" & +``` + +### Tests + +Add at least one array-input test alongside the scalar cases. Cover both orientations if both are realistic (`vertical array of …`, `horizontal array of …`). The harness asserts the spilled result with list-of-lists: + +```yaml + - name: vertical array of indices lifts + args: ['={10,20,30,40}', '={1;3;-1}'] + expected: [[10], [30], [40]] + + - name: horizontal array of indices lifts + args: ['={10,20,30,40}', '={1,2,-1}'] + expected: [[10, 20, 40]] +``` + +### Existing examples + +The `maps/` library, `NTH`, `CHARQ`, `REVERSESTRING`, and `TLOOKUP` all use this pattern — read any of them for a worked example. (Some lambdas already lift naturally via Excel's calc engine — e.g. `CONTAINS` via `XMATCH`, `CIRCPOS` via `MOD`. Those have confirmation tests but no dispatcher.) + ## Tests File Format The `.tests.yaml` file provides test cases for the automated test harness. @@ -184,6 +251,7 @@ Details to confirm: - **Parameters** — name, required/optional, description for each - **Formula approach** — brief description of the implementation - **Example** — sample call and expected result +- **Array-lifting** — does the LAMBDA fit the "scalar input → scalar output" shape (see Array-lifting Support above)? If yes, plan the dispatcher and name the param that will lift. If the issue is missing key details or the approach is ambiguous, ask the user to clarify rather than guessing.