namespace MS.Microservice.Core.Functional; /// /// 为 提供函数式编程核心操作的扩展方法集合。 /// /// /// /// 涵盖以下操作类别: /// /// Map(函子 / Functor):对 Some 中的值进行变换。 /// Bind(单子 / Monad):链接多个可能返回 None 的操作。 /// Match:穷举式模式匹配。 /// GetOrElse / OrElse:提取值或提供备用值。 /// Where(Filter):按谓词过滤。 /// ForEach:对 Some 执行副作用。 /// AsEnumerable:将 Option 转为序列。 /// Select / SelectMany:支持 LINQ 查询语法。 /// /// /// /// 来源:《C# 函数式编程》第 3.4 节 — 使用 Map 和 Bind 组合 Option 值。 /// /// public static class OptionExtensions { extension(Option opt) { // ── Map(函子操作)──────────────────────────────────────────────────── /// /// Map:将 Option 内部的值通过函数 变换为新类型, /// 返回包含变换结果的 Option。 /// /// 变换后的值类型。 /// 值变换函数。 /// /// 若当前 Option 为 Some(t),则返回 Some(f(t)); /// 若为 None,则直接返回 None, 不会被执行。 /// /// /// /// Map 体现了"函子"(Functor)的核心概念:在保持容器结构不变的情况下, /// 对容器内的值应用一个普通函数。 /// /// /// 此处使用 .NET 10 / C# 最新的 extension 语法声明扩展成员, /// 让函数式能力直接挂载到 上。 /// /// /// 来源:《C# 函数式编程》第 3.4 节 — Map(函子操作)。 /// /// public Option Map(Func f) => opt.Match( none: () => F.None, some: t => (Option)F.Some(f(t))); public Option> Map(Func f) => opt.Map(f.Curry()); // ── Bind(单子操作)─────────────────────────────────────────────────── /// /// Bind(又称 FlatMap / SelectMany): /// 将 Option 内部的值传递给一个返回 Option 的函数 , /// 并将结果"展平"为单层 Option。 /// /// 结果 Option 的值类型。 /// 接收值并返回 Option 的函数(Cross-world 函数)。 /// /// 若当前 Option 为 Some(t),则返回 f(t)(可能为 Some 或 None); /// 若为 None,则直接返回 None, 不会被执行。 /// /// /// /// Bind 体现了"单子"(Monad)的核心概念:将多个可能失败(返回 None)的操作 /// 串联在一起,只要任意一步返回 None,整条链路即短路为 None。 /// /// /// 来源:《C# 函数式编程》第 3.4 节 — Bind(单子操作)。 /// /// public Option Bind(Func> f) => opt.Match( none: () => F.None, some: f); /// /// 异步版本的 Bind,用于在 Some 分支中继续执行异步跨世界函数。 /// public Task> BindAsync(Func>> f) => opt.Match( none: () => Task.FromResult((Option)F.None), some: f); // ── Match(模式匹配)────────────────────────────────────────────────── /// /// Match 重载:以值而非函数的方式提供 None 分支的结果。 /// /// 返回结果类型。 /// None 分支返回的直接值。 /// Some 分支处理函数。 public TResult Match(TResult none, Func some) => opt.Match( none: () => none, some: some); /// /// 异步版本的 Match,便于在 API / AppService / Infrastructure 边界组合异步流程。 /// public Task MatchAsync(Func> none, Func> some) => opt.Match( none: none, some: some); // ── GetOrElse / OrElse ──────────────────────────────────────────────── /// /// GetOrElse:获取 Some 中的值;若为 None,则返回 。 /// public T GetOrElse(T defaultValue) => opt.Match( none: () => defaultValue, some: t => t); /// /// GetOrElse:获取 Some 中的值;若为 None,则调用 获取默认值。 /// /// 延迟计算版本,仅在 Option 为 None 时才执行 ,适合昂贵的默认值计算。 public T GetOrElse(Func fallback) => opt.Match( none: fallback, some: t => t); /// /// OrElse:若当前 Option 为 None,则用备用 Option 替代。 /// public Option OrElse(Option fallback) => opt.Match( none: () => fallback, some: _ => opt); /// /// OrElse:若当前 Option 为 None,则调用 获取备用 Option。 /// /// 延迟计算版本,仅在 Option 为 None 时才执行 public Option OrElse(Func> fallback) => opt.Match( none: fallback, some: _ => opt); // ── ForEach(副作用)───────────────────────────────────────────────── /// /// ForEach:若 Option 为 Some,则对内部值执行 副作用操作。 /// /// /// 返回 以保持函数式风格(避免出现无返回值的函数)。 /// /// /// ForEach 是唯一允许产生副作用(side-effect)的操作, /// 通常用于与外部系统(日志、IO 等)交互的最终步骤(边界处), /// 不应在纯业务逻辑的中间环节使用。 /// public Unit ForEach(Action action) => opt.Match( none: () => Unit.Default, some: t => { action(t); return Unit.Default; }); // ── Where(过滤)───────────────────────────────────────────────────── /// /// Where(Filter):若 Option 为 Some 且内部值满足 , /// 则保留原值;否则返回 None。 /// /// /// 来源:《C# 函数式编程》第 3.4 节 — Where/Filter 操作。 /// public Option Where(Func predicate) => opt.Match( none: () => F.None, some: t => predicate(t) ? opt : (Option)F.None); // ── AsEnumerable ───────────────────────────────────────────────────── /// /// 将 Option 转换为 : /// Some(t) 转换为只含一个元素的序列 [t],None 转换为空序列 []。 /// /// /// 此转换在需要将 Option 与 LINQ 序列操作(如 SelectMany)混用时非常有用。 /// public IEnumerable AsEnumerable() => opt.Match( none: Enumerable.Empty, some: t => Enumerable.Repeat(t, 1)); // ── LINQ 查询语法支持 ───────────────────────────────────────────────── /// /// 支持 LINQ 查询语法中的 select 子句(等同于 Map 操作)。 /// /// /// /// var result = from age in maybeAge /// select age + 1; /// /// public Option Select(Func f) => opt.Map(f); /// /// 支持 LINQ 查询语法中的多级 from 子句(等同于 Bind + Map 操作)。 /// /// /// /// var result = from age in maybeAge /// from email in maybeEmail /// select $"{email} is {age}"; /// /// public Option SelectMany(Func> bind, Func project) => opt.Match( none: () => F.None, some: t => bind(t).Match( none: () => F.None, some: bound => (Option)F.Some(project(t, bound)))); } // ── Apply(应用函子 / Applicative Functor)─────────────────────────────── extension(Option> optF) { /// /// Apply(应用函子):将包裹在 中的函数应用到 /// 同样包裹在 中的参数上。 /// /// 包含待应用参数的 Option。 /// /// 若函数与参数都为 Some,则返回 Some(f(arg)); /// 任意一方为 None,则返回 None。 /// /// /// /// Apply 实现了"应用函子"(Applicative Functor)的核心操作: /// 允许将包裹在容器中的多元函数逐步应用到各自包裹在容器中的参数上, /// 最终仍保持在容器语义中,None 在任意一环即短路整条链。 /// /// /// 典型用法(与 Map 协同实现多参数可选值的安全组合): /// /// Func<int, int, int> add = (x, y) => x + y; /// Option<int> optX = F.Some(3), optY = F.Some(4); /// /// // 先用 Map 提升为 Option<Func<int, int>>,再用 Apply 应用第二个参数 /// Option<int> result = optX.Map(add).Apply(optY); // Some(7) /// /// /// /// 来源:《C# 函数式编程》第 5.3 节 — Apply 与应用函子。 /// /// public Option Apply(Option arg) => optF.Match( none: () => F.None, some: f => arg.Map(f)); } extension(Option> optF) { /// /// Apply 重载:将包裹在 Option 中的二元函数部分应用第一个参数, /// 返回包裹在 Option 中的一元函数。 /// /// /// 对应签名:A<T1 -> T2 -> R> -> A<T1> -> A<T2 -> R>。 /// 典型用法:先 Map(f) 将二元函数提升,再逐步 Apply 各参数: /// /// optX.Map(multiply).Apply(optY) // Some(result) /// /// 来源:《C# 函数式编程》第 7 章 — 高级界域中的 Apply 重载。 /// public Option> Apply(Option arg) => optF.Match( none: () => F.None, some: f => arg.Map(f.Curry())); } extension(Option> optF) { /// /// Apply 重载:将包裹在 Option 中的三元函数部分应用第一个参数, /// 返回包裹在 Option 中的柯里化二元函数。 /// /// /// 对应签名:A<T1 -> T2 -> T3 -> R> -> A<T1> -> A<T2 -> T3 -> R>。 /// 书中箭头符号 T2→T3→R 为柯里化形式,对应 /// 其中 TResult 为 Func<T3, R>。 /// 来源:《C# 函数式编程》第 7 章 — 高级界域中的 Apply 重载。 /// public Option>> Apply(Option arg) => optF.Match( none: () => (Option>>)F.None, some: f => arg.Map(f.Curry())); //// 等同于下面 //=> Apply(optF.Map(FuncExtensions.Curry), arg); } }