尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

effect-smol Optic 函数式光学:Lens、Prism 与 Traversal 完整指南

effect-smol Optic 函数式光学:Lens、Prism 与 Traversal 完整指南 effect-smol Optic 函数式光学Lens、Prism 与 Traversal 完整指南【免费下载链接】effect-smolCore libraries and experimental work for Effect v4项目地址: https://gitcode.com/GitHub_Trending/ef/effect-smol如果你在用 TypeScript 处理嵌套不可变数据一定被改一个字段要重写整棵对象树折磨过。effect-smol项目中的Optic 模块函数式光学正是解药它让你像相机变焦一样用Lens、Prism、Traversal等光学元件安全地聚焦、读取和更新嵌套结构中的任意部分——且全程不修改原数据。 什么是函数式光学把光学想象成一个可复用的聚焦器它描述了在哪里看、在哪里改。每个光学都支持三个核心操作操作含义类比get读取聚焦到的值用放大镜看细节replace替换聚焦值换掉那颗螺丝modify用函数变换聚焦值把螺丝拧紧一点构建方式像搭积木先聚焦company再聚焦address再聚焦street层层下钻。定义一次到处复用。 官方文档packages/effect/OPTIC.md 核心源码packages/effect/src/Optic.ts 五种光学一分钟认识家族成员Optic 模块提供了五种光学类型它们共享同一套组合方式光学聚焦目标聚焦后一定有值吗典型场景Iso无损可逆的类型转换✅ 必有摄氏度 ↔ 华氏度Lens一定存在的字段✅ 必有user.namePrism联合类型的某一个分支⚠️ 可能没有匹配_tag: AOptional可能存在的焦点⚠️ 可能没有可选字段、数组下标Traversal集合中的零或多个元素⚠️ 多个遍历数组所有正数一句话记忆Iso 最强、Lens 最常用、Prism 管联合、Traversal 管批量。类型定义可以在源码中查看Iso接口packages/effect/src/Optic.ts#L66Lens接口packages/effect/src/Optic.ts#L144Prism接口packages/effect/src/Optic.ts#L232Traversal接口packages/effect/src/Optic.ts#L1033⚡ 快速上手三行代码聚焦字段先看最小示例——聚焦对象S中的字段aimport { Optic } from effect type S { readonly a: number } const _a Optic.idS().key(a) _a.get({ a: 1 }) // 1 读取 _a.replace(2, { a: 1 }) // { a: 2 } 替换 _a.modify((n) n 1)({ a: 1 }) // { a: 2 } 变换注意Optic.idS()它是身份光学identity Iso表示先聚焦整个对象本身是所有光学的起点。嵌套数据层层下钻到深层字段假设要修改员工信息中「公司 → 地址 → 街道 → 名称」这个深埋的字段const _streetName Optic.idEmployee() .key(company) // 进入 company .key(address) // 进入 address .key(street) // 进入 street .key(name) // 锁定 name const capitalizeStreetName _streetName.modify(String.capitalize)调用capitalizeStreetName(from)后只有street.name的首字母被大写其余数据原封不动且原对象保持不可变。路径只定义一次改数据结构时只需改这一个地方。 Lens 实战安全处理可选字段Lens 聚焦一定存在的字段但 Optic 对可选数据也有声明式处理关键是区分两种语义key(a)值为undefined时保留该键 →{ a: undefined }optionalKey(a)值为undefined时删除该键 →{}const _a Optic.idS().optionalKey(a) _a.replace(2, {}) // { a: 2 } 从无到有 _a.replace(undefined, { a: 1 }) // {} 从有到无键被移除这套语义让清除字段这类操作变得干净利落无需手写delete或解构技巧。 Prism 实战安全聚焦联合类型的一个分支联合类型tagged union是函数式编程的常客。直接用key访问某个分支的字段会踩空而Prism通过tag方法聚焦特定分支type S | { readonly _tag: A; readonly a: number } | { readonly _tag: B; readonly b: number } const _a Optic.idS().tag(A).key(a) _a.replace(2, { _tag: A, a: 1 }) // { _tag: A, a: 2 } 匹配更新 _a.replace(2, { _tag: B, b: 1 }) // { _tag: B, b: 1 } 不匹配原样返回约定用_tag字段标识变体。不匹配时优雅地无操作而不是抛错——这让联合类型的更新既安全又可预测。 Traversal 实战批量处理集合元素Traversal聚焦集合中的多个元素。配合forEach 条件判断可以精准批处理const _positive Optic.idS() .key(a) // 聚焦数组 .forEach((item) item.check(Schema.isGreaterThan(0))) // 只保留正数 const addOne _positive.modifyAll((n) n 1) addOne({ a: [1, -2, 3] }) // { a: [2, -2, 4] } 负数不受影响 技术细节与其他光学库不同这里的Traversal不是独立类型而是被建模为聚焦到ReadonlyArrayA的Optional。要对每个元素继续下钻通过forEach传入的IsoA, A与后续光学组合即可。 进阶从 Schema 一键生成光学如果你有 Effect 的 Schema可以直接生成光学起点省去手写路径import { Schema } from effect const schema Schema.Struct({ a: Schema.String, b: Schema.Number }) const _b Schema.toIso(schema).key(b) // 自动获得类型安全的 LensSchema.Class也开箱即用类实例可以直接作为光标的操作对象。相关测试见 packages/effect/test/schema/toIso.test.ts。⚖️ 既然有 Immer为什么还要 OpticImmer 让写可变代码、得到不可变结果日常够用。但当你面对深层嵌套 联合类型 可复用更新逻辑时Optic 的优势很明显维度ImmerOptic路径导航每次更新都手写一遍定义一次 Lens到处复用可选字段手动if守卫notUndefined()声明式表达结构演化散落各处的路径同步修改改一处光学定义全局生效核心哲学是组合胜过嵌套把小光学拼成大光学代码保持扁平、可测试、可跨模块共享。 选型速查表不知道怎么选光学对照这张表数据形状用这个一定存在的字段key可选字段保留undefinedkey可选字段清除时删键optionalKey联合类型的某个分支tagRecord / 数组下标at过滤并批量更新元素forEachcheck/notUndefined另外如果聚焦失败需要排查用getResult显式拿到成功/失败结果比静默跳过更易调试。 开始学习 Optic资源路径官方完整文档含全部示例packages/effect/OPTIC.md核心实现类型 构造函数 操作packages/effect/src/Optic.ts单元测试行为参考packages/effect/test/Optic.test.tsSchema 生成光学测试packages/effect/test/schema/toIso.test.ts上手路线建议先用key链式聚焦嵌套字段建立手感 → 再用tag处理联合类型 → 最后用forEachcheck玩转批量更新。配合 packages/effect/OPTIC.md 中的完整示例你就能在不可变数据的世界里自由变焦了。【免费下载链接】effect-smolCore libraries and experimental work for Effect v4项目地址: https://gitcode.com/GitHub_Trending/ef/effect-smol创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表