Skip to content

Simplify Optic composition and add regression coverage - #6735

Merged
gcanti merged 1 commit into
mainfrom
refactor/optic-composition
Jul 29, 2026
Merged

Simplify Optic composition and add regression coverage#6735
gcanti merged 1 commit into
mainfrom
refactor/optic-composition

Conversation

@gcanti

@gcanti gcanti commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes

    • Composed Iso and Prism updates no longer require reading the original value before writing.
    • Optional setter failures now propagate correctly through composed optics.
    • modifyAll and getAll handle traversal failures more reliably.
    • Object updates preserve own __proto__ properties safely.
    • Identity composition and grouped composition now behave consistently.
  • API Improvements

    • notUndefined retains optional behavior when used with an Optional.
    • Internal implementation details are no longer exposed on public optic types.
    • Improved type consistency across composed optics and result-handling APIs.

@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Jul 29, 2026
@changeset-bot

changeset-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b252936

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 29 packages
Name Type
effect Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node-shared Patch
@effect/platform-node Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/openapi-generator Patch

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

@effect-slopcop effect-slopcop Bot added 4.0 enhancement New feature or request labels Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Optic construction and composition are refactored to use primitive nodes and compiled operations. Public Optional typing hides internal nodes and preserves Optional through notUndefined. Tests cover composition, failure paths, API types, and benchmark measurement changes.

Changes

Optic composition and public API

Layer / File(s) Summary
Primitive optic compilation and composition
packages/effect/src/Optic.ts
Optic chains use primitive nodes, normalized operations, kind composition, and compiled get/set functions.
Public optic contracts and built-in wiring
packages/effect/src/Optic.ts
The Optional interface hides node, notUndefined preserves Optional typing, and built-in optics use the new representation.
Composition and failure-path validation
packages/effect/test/Optic.test.ts, packages/effect/typetest/Optic.tst.ts
Tests cover composition behavior, setter and traversal failures, prototype preservation, Option and Result updates, and revised public types.
Benchmark instrumentation and release metadata
packages/effect/benchmark/schema/Optic.ts, .changeset/simplify-optic-composition.md
Benchmarks batch operations with high-resolution timing and custom formatting; the changeset records the patch release fixes.

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
Loading

Suggested labels: bug

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the bug Something isn't working label Jul 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/effect/src/Optic.ts (1)

1244-1259: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Forward pass computes one get too many.

The loop runs op.get(s) for every step including the last, but sources[len - 1] is filled before that final call, so the last step's get result is never used. For deep chains this is wasted work on the write path (and for at-style steps it means an extra Object.hasOwn + Result allocation 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] = s

Note this is a behavioral change for the last step when its get fails but its set would 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6d2a942 and b252936.

📒 Files selected for processing (5)
  • .changeset/simplify-optic-composition.md
  • packages/effect/benchmark/schema/Optic.ts
  • packages/effect/src/Optic.ts
  • packages/effect/test/Optic.test.ts
  • packages/effect/typetest/Optic.tst.ts

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

File Name Current Size Previous Size Difference
basic.ts 6.63 KB 6.63 KB 0.00 KB (0.00%)
batching.ts 9.42 KB 9.42 KB 0.00 KB (0.00%)
brand.ts 6.31 KB 6.31 KB 0.00 KB (0.00%)
cache.ts 10.12 KB 10.12 KB 0.00 KB (0.00%)
config.ts 19.90 KB 19.90 KB 0.00 KB (0.00%)
differ.ts 20.03 KB 20.03 KB 0.00 KB (0.00%)
http-client.ts 20.94 KB 20.94 KB 0.00 KB (0.00%)
logger.ts 10.28 KB 10.28 KB 0.00 KB (0.00%)
metric.ts 8.55 KB 8.55 KB 0.00 KB (0.00%)
optic.ts 7.33 KB 7.46 KB -0.14 KB (-1.82%)
pubsub.ts 14.26 KB 14.26 KB 0.00 KB (0.00%)
queue.ts 11.09 KB 11.09 KB 0.00 KB (0.00%)
schedule.ts 10.27 KB 10.27 KB 0.00 KB (0.00%)
schema-class.ts 18.86 KB 18.86 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.78 KB 28.78 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 25.09 KB 25.09 KB 0.00 KB (0.00%)
schema-string-transformation.ts 12.95 KB 12.95 KB 0.00 KB (0.00%)
schema-string.ts 10.65 KB 10.65 KB 0.00 KB (0.00%)
schema-template-literal.ts 14.85 KB 14.85 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 21.66 KB 21.66 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.10 KB 24.10 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 19.00 KB 19.00 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 18.73 KB 18.73 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.59 KB 18.59 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.23 KB 22.23 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.27 KB 19.27 KB 0.00 KB (0.00%)
schema.ts 18.12 KB 18.12 KB 0.00 KB (0.00%)
stm.ts 12.05 KB 12.05 KB 0.00 KB (0.00%)
stream.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)

@gcanti gcanti removed the bug Something isn't working label Jul 29, 2026
@gcanti
gcanti merged commit 1284aa1 into main Jul 29, 2026
15 checks passed
@gcanti
gcanti deleted the refactor/optic-composition branch July 29, 2026 08:54
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Done in PR Backlog Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant