![Eff核心概念详解:读懂Eff[R,A]类型、Fx效果栈与Member类型类](http://pic.xiahunao.cn/yaotu/Eff核心概念详解:读懂Eff[R,A]类型、Fx效果栈与Member类型类)
Eff核心概念详解读懂Eff[R,A]类型、Fx效果栈与Member类型类【免费下载链接】effEff monad for cats - https://atnos-org.github.io/eff项目地址: https://gitcode.com/gh_mirrors/eff/effeff是 Atnos 组织出品、基于 cats 的可扩展效果Extensible Effects函数式编程库。对于想在 Scala 中以纯函数方式组合副作用的开发者来说理解它的三大核心——Eff[R,A] 类型、Fx 效果栈与Member 类型类——是入门的第一步。本文用最通俗的方式带你一文读懂这套类型系统。 为什么需要 Eff传统 Monad Transformer 的痛点在 Scala 函数式编程中我们习惯用Option、Either、State等 Monad 来表示副作用。但当多种副作用同时存在时传统做法需要用Monad Transformer单子转换器一层层嵌套类型签名会迅速变成难以阅读的类型迷宫。Eff 提供了一条新路用一个统一的单子类型Eff[R, A]承载任意数量的效果效果被平铺在一个栈 R 中需要哪个就用哪个。这就是可扩展效果的核心思想。 核心一Eff[R, A] 类型——一个类型表达一切效果Eff[R, A]是 eff 库中最核心的类型它的两个参数含义如下R栈类型描述这次计算可能用到哪些效果即效果栈A值类型计算最终产出的纯值类型。可以把Eff[R, A]理解为一个尚未执行的程序描述它记录了要做什么比如读配置、写日志、可能失败但执行发生在解释interpret阶段。内部只有三种形态打开 Eff.scala你会发现Eff是个密封抽象类内部只有三种情况构造器含义通俗理解Pure纯值什么都不做直接返回一个 A 值Impure单个效果 续延先执行这个效果拿到结果 X 后接着做本质是一次 flatMapImpureAp多个独立效果 映射函数并行地做这几件事最后把结果合起来本质是 sequence map其中Continuation续延是 Eff 的灵魂它保存了效果执行完之后接下来要做什么让解释器可以按自己的节奏逐层剥开程序。 关键理解编写代码时你在描述效果运行代码时解释器才真正执行效果——这就是自由的自由度所在。 核心二Fx 效果栈——效果的类型化目录效果栈 R 不是简单的列表而是一棵平衡的类型树定义在 Fx.scala 中Fx1[T]、Fx2[T1, T2]、Fx3[T1, T2, T3]容纳 1~3 个效果的节点FxAppend[L, R]把两棵子树拼接起来的节点NoFx空栈表示纯计算。库提供了fx1到fx12的类型别名让类型签名保持紧凑。例如一个包含错误处理、控制台输出、警告收集、惰性求值四种效果的栈可以写成type ActionStack Fx.fx4[ErrorOrOk, Console, Warnings, Eval]这个例子来自 Action.scala是官方推荐的效果栈 自定义效果完整示范非常值得阅读。Union效果在栈中的占位符当某个效果被放入栈中时它以一个Union值的形式存在见 Union.scala。Union 相当于 C/C 中的tagged union带标签的联合体UnionTagged(value, index)记录值本身以及它在栈中的位置编号UnionAppendL/UnionAppendR表示该效果位于拼接树的左子树或右子树。正是这个位置编号机制让解释器能够精准地找到我要解释的那个效果而不会与栈中其他效果混淆。 核心三Member 类型类——效果的成员证明Member 是连接具体效果与效果栈的桥梁。它的源码位于 Member.scala核心家族包括三个成员类型类能力使用场景MemberIn[T, R]把效果 T注入栈 R编写效果声明R 中包含 T所以我可以产生 TMemberInOut[T, R]注入 从栈中提取翻译效果translate、拦截效果interceptMember[T, R, U]注入 提取 移除T 后得到剩余栈 U解释器执行完 T 后把它从栈中拿走Member 的实例由编译器自动推导只要T出现在栈R中编译器就能找到对应的 given 实例通过MemberInLower1~MemberInLower5等层级隐式规则逐级匹配。如果效果不在栈里你会得到一个非常友好的编译错误提示The effect T is not part of the stack R——这比运行期异常安全得多。⚙️ 三者如何协作一次完整的运行流程以官方 Readme 中的经典示例见 ReadmeSpec.scala为例流程可以概括为四步定义栈type Stack Fx.fx3[ReaderInt, WriterString, Eval]——声明程序用到的全部效果编写程序在 for 推导式中自由混用askReader、tellWriter、delayEval返回Eff[Stack, Int]逐层解释run(runEval(runWriter(runReader(5)(action))))——从栈外向内一个解释器处理一种效果每处理一层就从栈中移除该效果得到纯值最终输出(32, List(START: the start value is 5, END))一个完全纯净的元组。解释逻辑的核心实现在 Interpret.scalarunInterpreter递归地匹配Eff的三种形态用 Member 的project方法判断当前效果是不是我要解释的那个是则调用解释器处理否则原样保留交给下一个解释器。️ 源码地图去哪里看更多模块路径内容核心core/src/main/scala/org/atnos/eff/Eff、Fx、Union、Member、Interpret 等核心类型Readerreader/src/main/scala/org/atnos/eff/ReaderEffect.scala环境读取效果Writerwriter/src/main/scala/org/atnos/eff/WriterEffect.scala日志/记录效果Statestate/src/main/scala/org/atnos/eff/StateEffect.scala状态效果Errorerror/src/main/scala/org/atnos/eff/ErrorEffect.scala错误处理效果文档源码doc/src/test/scala/org/atnos/site/官方用户指南与教程Tutorial、CreateEffects 等官方文档源码中的Tutorial.scala提供了一篇类似 cats Free 单子的完整教程CreateEffects.scala则手把手教你创建自定义效果是理解本文概念的绝佳延伸阅读。✅ 新手速记卡Eff[R, A]一个类型装下所有效果R 是效果栈A 是返回值Fx效果栈的类型化树fx1~fx12是常用别名Union效果在栈中的带标签占位符靠位置编号定位Member编译器自动生成的成员证明分 In / InOut / 完全成员三级解释每个解释器只负责一种效果逐层剥栈最终得到纯值。掌握这四块拼图你就能读懂 eff 的全部类型签名并顺利开始自己的可扩展效果之旅。【免费下载链接】effEff monad for cats - https://atnos-org.github.io/eff项目地址: https://gitcode.com/gh_mirrors/eff/eff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考