Simplify Optic composition and add regression coverage - #6735
Conversation
🦋 Changeset detectedLatest commit: b252936 The changes in this PR will be included in the next version bump. This PR includes changesets to release 29 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughOptic construction and composition are refactored to use primitive nodes and compiled operations. Public Optional typing hides internal nodes and preserves Optional through ChangesOptic composition and public API
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant Optic
participant CompiledGetSet
participant Source
Caller->>Optic: compose optics
Optic->>CompiledGetSet: compile normalized chain
Caller->>Optic: get or set value
Optic->>Source: execute compiled get/set
Source-->>Optic: result or failure
Optic-->>Caller: optic operation result
Suggested labels: 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/effect/src/Optic.ts (1)
1244-1259: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueForward pass computes one
gettoo many.The loop runs
op.get(s)for every step including the last, butsources[len - 1]is filled before that final call, so the last step'sgetresult is never used. For deep chains this is wasted work on the write path (and forat-style steps it means an extraObject.hasOwn+Resultallocation per replace).♻️ Skip the unused final read
return (a, s) => { const len = nodes.length const sources = new Array(len) - for (let i = 0; i < len; i++) { + for (let i = 0; i < len - 1; i++) { sources[i] = s const op = nodes[i] if (hasFailingGet(op.kind)) { const result = op.get(s) if (Result.isFailure(result)) { return result } s = result.success } else { s = op.get(s) } } + sources[len - 1] = sNote this is a behavioral change for the last step when its
getfails but itssetwould succeed (currently such a chain fails the whole replace). Worth confirming which semantics you want before applying.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/effect/src/Optic.ts` around lines 1244 - 1259, Update the forward pass around the loop over nodes so it performs get only for steps before the final node, while still recording each source needed by the write path. Preserve failing-get propagation for intermediate steps, and confirm the intended last-step behavior: do not reject replace based solely on the final get failure when that step’s set can succeed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/effect/src/Optic.ts`:
- Around line 1244-1259: Update the forward pass around the loop over nodes so
it performs get only for steps before the final node, while still recording each
source needed by the write path. Preserve failing-get propagation for
intermediate steps, and confirm the intended last-step behavior: do not reject
replace based solely on the final get failure when that step’s set can succeed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5528d131-2dc2-4192-b205-01a920058c18
📒 Files selected for processing (5)
.changeset/simplify-optic-composition.mdpackages/effect/benchmark/schema/Optic.tspackages/effect/src/Optic.tspackages/effect/test/Optic.test.tspackages/effect/typetest/Optic.tst.ts
Bundle Size Analysis
|
Summary by CodeRabbit
Bug Fixes
IsoandPrismupdates no longer require reading the original value before writing.modifyAllandgetAllhandle traversal failures more reliably.__proto__properties safely.API Improvements
notUndefinedretains optional behavior when used with anOptional.