
后端【免费下载链接】language-extC# pure functional programming framework - come and get declarative!项目地址https://gitcode.com/gh_mirrors/la/language-ext点击查看免费下载本指南聚焦 language-extC# 纯函数式编程框架核心模块 LanguageExt.Core/Lens/README.md 所定义的 Lens 抽象在不可变数据结构中向内查看look inside并随后更新update某个值同时通过返回新版本结构来彻底规避突变。读完本文你将掌握LensA, B的构造与组合、lens函数与|操作符的用法、与 Map/Arr/Lst 等不可变集合itemLens 的协作以及从 Lens 到 Prism 的转换能够直接在项目中写出纯函数式的嵌套更新代码。一、Lens 解决什么问题在不可变编程模型中嵌套结构如Book → Editor → Car → Mileage的更新令人头痛没有指针可改只能层层重建外层对象代码会迅速退化为大量样板。Lens 正是为此而生。原文档 LanguageExt.Core/Lens/README.md 给出两句话的准确定义Lenses allow you to look inside a structure to access its value and then subsequently update that value. But because we dont want to do mutation, the updaters create new versions of the underlying structure.拆解为三个要点查看accessLens 提供Get能从外层结构A中取出内层值B更新updateLens 提供Set能把新的内层值B写回外层结构A不突变no mutationSet/Update返回的是外层结构的新版本原结构保持不变——这与 language-ext 中所有不可变集合Map、Arr、Lst等的持久化语义完全一致。需要强调的是这里的更新是函数式的写回操作实际构造了一条旧结构 → 新结构的纯函数管道原对象不会被修改因此天然适合多线程共享与函数式组合。二、核心类型 Lens〈A, B〉Get 与 SetF 的二元结构Lens 的底层实现位于 LanguageExt.Core/Lens/LensAB.cs它是一个readonly struct只携带两个字段public readonly struct LensA, B { public readonly FuncA, B Get; // 从 A 中取出 B public readonly FuncB, FuncA, A SetF; // 给定新值 B返回 A - A 的更新函数 public A Set(B value, A cont) SetF(value)(cont); public static LensA, B New(FuncA, B Get, FuncB, FuncA, A Set) new(Get, Set); public FuncA, A Update(FuncB, B f) a Set(f(Get(a)), a); public A Update(FuncB, B f, A value) Set(f(Get(value)), value); }这里的类型签名值得细读Get : A - B——读取方向SetF : B - A - A——柯里化的写入方向先接收新值B返回一个A → A的转换函数调用该函数时传入旧结构、产出新结构。这种柯里化设计正是 Lens 能被顺序组合见第四节的关键Update(FuncB, B f)——就地变换modify先Get出内层值用f变换再Set回去。这是比取出→计算→写回三步更原子、更优雅的 API。从源码结构看Set与Update都是GetSetF的组合因此只要正确构造New(Get, Set)四条操作天然成立。任何满足Set(v, a)后再Get能还原v的 Lens 即为well-behaved行为良好的双向变换这也是该类型注释 well-behaved bidirectional transformations 的由来。三、构造 Lens 的常用工厂函数大多数情况下不必手写NewLanguageExt.Core/Lens/Lens.cs 提供了现成的工厂工厂函数签名语义identityA()LensA, A恒等透镜Get原样返回Set整体替换fstA, B()Lens(A, B), A二元组/三元组/四元组的第一个元素sndA, B()Lens(A, B), B第二个元素thrdA, B, C()Lens(A, B, C), C第三个元素condA, B(pred, Then, Else)LensA, B按谓词动态选择两个 Lens 之一tuple(First, Second)Lens(A,B), (C,D)将两个 Lens 合并作用于二元组enumMap(la)LensIEnumerableA, IEnumerableB把作用于单个元素A的 Lens 提升到整个序列以fst为例其实现展示了查看与更新必须对称的写法public static Lens(A, B), A fstA, B() Lens(A, B), A.New( Get: ab ab.Item1, Set: a ab (a, ab.Item2));Set构造的新二元组保留原组的第二个元素只替换第一个元素——这正是创建新版本、不碰旧结构的具体体现。cond则适合同一结构中按条件走不同字段路径的场景Get/Set前先用pred(a)决定采用Then还是Else透镜。四、顺序组合lens 函数与 | 操作符单个 Lens 只能访问一层真实业务往往需要穿透多层嵌套。language-ext 提供了两条等价的组合途径。4.1 Prelude 的 lens 函数LanguageExt.Core/Lens/Prelude.Lens.cs 中lens提供了从 2 个到 10 个 Lens 的重载逐一顺序组合。以三参版本为例public static LensA, D lensA, B, C, D(LensA, B la, LensB, C lb, LensC, D lc) LensA, D.New( Get: a lc.Get(lb.Get(la.Get(a))), // 沿路径层层向下取 Set: v la.Update(lb.Update(lc.SetF(v)))); // 从最内层开始层层向上重建注意Set的组装顺序与Get完全相反Set先把新值交给最内层lc产生C→C的更新再被lb.Update提升为B→B最后被la.Update提升为A→A。Update在此充当把内层变换逐级外提的粘合剂这正是第二节柯里化SetF设计的意义。4.2 中缀操作符 |LanguageExt.Core/Lens/Lens.Operators.cs 为LensA, B定义了|操作符语义与lens相同写作更接近管道风格public static LensA, C operator |(LensA, B lhs, LensB, C rhs) LensA, C.New( Get: a rhs.Get(lhs.Get(a)), Set: v lhs.Update(rhs.SetF(v)));于是Book.editor | Editor.car | Car.mileage与lens(Book.editor, Editor.car, Car.mileage)完全等价可按团队风格选用。五、与不可变集合协作item 系列 LensLens 的实用价值很大一部分来自集合索引。language-ext 为各不可变集合内置了itemLens使其可以直接作为组合链的中间环节Map.csMapK, V.item(key)与MapK, V.itemOrNone(key)Arr.csArrA.item(index)Lst.csLstA.item(index)同理可查HashMap、TrackingHashMap等类型中的同名工厂。Map的实现最典型——它把按键取值封装为可组合的透镜public static LensMapK, V, V item(K key) LensMapK, V, V.New( Get: la la[key], Set: a la la.AddOrUpdate(key, a)); // 返回新 Map原 Map 不变itemOrNone则返回LensMapK, V, OptionVGet用Find得到OptionSet时若为Some(x)则AddOrUpdate若为None则Remove——这把键可能不存在的细节也封装进了透镜语义组合链可放心使用。六、测试用例验证从 Record 嵌套结构看完整用法LanguageExt.Tests/LensTests.cs 提供了 Lens 在Record类型上的完整演示。需要说明的是该文件当前整体处于注释状态文件头标注 TODO: Restore when we have SourceGen即等待源代码生成器恢复后启用但它完整展示了[WithLens]特性配合Record派生类生成字段级 Lens 的设想用法与 LanguageExt.Core/Record 的生成器设计一致因此其代码仍具权威参考价值。6.1 多层嵌套的读取与更新定义Car → Editor → Book三层 Record均标注[WithLens]以便生成Car.mileage、Editor.car、Book.editor等静态 Lensvar car new Car(Maserati, Ghibli, 20000); var editor new Editor(Joe Bloggs, 50000, car); var book new Book(Editors of the World, Sarah Bloggs, editor); var bookEditorCarMileage lens(Book.editor, Editor.car, Car.mileage); var mileage bookEditorCarMileage.Get(book); // 读取20000 var book2 bookEditorCarMileage.Set(25000, book);// 更新返回新 Book Assert.True(book2.Editor.Car.Mileage 25000); // 原 book 及其 editor、car 均未被修改Get沿Book → Editor → Car → Mileage下行取值Set则从最内层Mileage开始逐级生成新的Car、Editor、Book最终book2与原book共享未被改动的部分这是持久化结构天然带来的结构共享structural sharing收益。6.2 集合元素级更新测试还演示了 Lens 与Map的组合——按预约 id 更新某个预约的状态var person new Person(Paul, Louth, Map( (1, new Appt(1, DateTime.Parse(1/1/2010), ApptState.NotArrived)), (2, new Appt(2, DateTime.Parse(2/1/2010), ApptState.NotArrived)), (3, new Appt(3, DateTime.Parse(3/1/2010), ApptState.NotArrived)))); LensPerson, ApptState arrive(int id) lens(Person.appts, Mapint, Appt.item(id), Appt.state); var person2 arrive(2).Set(ApptState.Arrived, person); Assert.True(person2.Appts[2].State ApptState.Arrived);组合链Person.appts → Map.item(2) → Appt.state将人 → 预约表 → 指定预约 → 状态四层访问压缩为一条声明式管道这正是第五节itemLens 的实战价值。七、从 Lens 到 Prism处理可能缺失的值当目标值可能不存在时Lens 升级为 Prism。姊妹文档 LanguageExt.Core/Prism/README.md 说明了两者的唯一区别Prism 的 getter 返回可选值FuncA, OptionB因此Get失败时更新会安全地落到原值。PrismAB.cs 提供两种构造PrismA, B.New(LensA, B)与PrismA, B.New(LensA, OptionB)并定义了从Lens到Prism的隐式转换Prelude_Prism.cs 提供prism(la)辅助函数以及与lens结构对称的 210 个 Prism 顺序组合重载Get使用Bind串联 OptionSet使用pa.Update/pb.Update提升Lens.cs 还提供扩展方法ToPrism()可一键把LensA, B或LensA, OptionB转成 Prism。典型用法Mapint, Appt.itemOrNone(5)得到LensMapint, Appt, OptionAppt经ToPrism()或prism(...)转为PrismMapint, Appt, Appt后Get返回OptionApptUpdate在键不存在时直接返回原 Map参见 PrismAB.cs 中Update的IfNone(a)兜底。八、适用范围与使用建议结合 LensTests.cs、Lens.cs 与集合item实现可以总结出以下实践准则读取深度嵌套的不可变数据时lens(...)/|组合链比逐层foreach/属性访问更声明式、更易复用更新深层字段时优先使用Set或Update(f)让 Lens 替你完成整条路径的重建避免手写多层with表达式值可能缺失时改用 PrismitemOrNoneToPrism把 Option 处理并入透镜语义Record 类型可借助[WithLens]特性见 LanguageExt.Tests/LensTests.cs生成字段级 Lens配合RecordT使用所有操作均返回新结构原对象保持不可变可安全用于并发场景与缓存共享。Lens 是 language-ext 不可变优先设计哲学在数据访问层的落点它把查看 更新封装为一对对称函数让嵌套结构的读写变得可组合、可复用、无副作用。结合lens组合函数、|操作符、集合item工厂与 Prism 转换你可以用极少的样板代码完成任意深度的持久化更新这正是纯函数式编程在 C# 中落地的重要一步。赞分享后端【免费下载链接】language-extC# pure functional programming framework - come and get declarative!项目地址https://gitcode.com/gh_mirrors/la/language-ext点击查看免费下载相关推荐Redux中的不可变更新模式详解Redux中的不可变更新模式详解 前言为什么不可变性如此重要 在Redux应用中 不可变性Immutability 是状态管理的核心原则。想象一下这样前端Python通达信数据读取终极指南快速获取股票行情与财务数据Python通达信数据读取终极指南快速获取股票行情与财务数据 想要用Python轻松读取通达信数据吗mootdx就是你的完美解决方案这是一个专门为Pyth金融科技数据分析language-ext 不可变集合指南Seq、Lst、HashMap、Map 与 HashSet、Set 的选型与性能解读language ext 不可变集合指南Seq、Lst、HashMap、Map 与 HashSet、Set 的选型与性能解读 导读 本文围绕 languag后端上一篇KMS_VL_ALL_AIOWindows和Office一键激活终极解决方案下一篇如何免费创建专业吉他谱TuxGuitar开源吉他编辑器完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考