effect-smol 修复 Effect.partition回归 Effect v3 的失败收集语义【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code本篇基于 effect-smol 仓库中的一条 changeset 变更记录讲解Effect.partition在 v4smol 重写版中的行为修正恢复为 Effect v3 的从错误通道收集失败、自身永不失败语义。读完你可以准确理解partition的返回类型、错误处理路径以及该实现在源码层面如何通过forEachresultArr.partition组合实现这一语义。变更记录changeset 说了什么本次变更以标准 changesets 文件格式记录内容如下--- effect: patch --- Revert Effect.partition to Effect v3 behavior by accumulating failures from the effect error channel and never failing.从字段与目录结构可以读出三条事实影响包与等级effect: patch表示该变更只涉及effect包且定级为 patch兼容性修复级别符合恢复旧版既有行为的定位变更性质Revert ... to Effect v3 behavior说明这不是新特性而是修正 v4 重写过程中偏离 v3 语义的行为——partition必须从 effect 的错误通道error channel累积失败且自身永不失败所处发布模式该文件位于.changeset/pre/子目录而非.changeset/根目录从 changesets 的目录约定可以推断仓库当前处于 pre预发布发布模式下这批记录将在下一次预发布版本中随版本发布。仓库中其他同格式的变更记录如 .changeset/pre 目录下的add-chunk-schema.md等均采用相同结构可对照理解这一发布流程。v3 语义的核心返回类型即契约partition的公开 API 声明在 Effect.tsexport const partition: { A, B, E, R( f: (a: A, i: number) EffectB, E, R, options?: { readonly concurrency?: Concurrency | undefined } ): (elements: IterableA) Effect[excluded: ArrayE, satisfying: ArrayB], never, R A, B, E, R( elements: IterableA, f: (a: A, i: number) EffectB, E, R, options?: { readonly concurrency?: Concurrency | undefined } ): Effect[excluded: ArrayE, satisfying: ArrayB], never, R }changeset 所强调的v3 behavior在类型签名上体现为两个硬性约束错误通道为never返回的Effect第三个泛型参数是never即调用方在类型层面就不可能拿到失败结果。即使批量操作中某个元素的效果失败partition本身也只会成功完成元组结构固定为[excluded, satisfying]第一个元素excluded是失败数组元素类型为错误类型E第二个元素satisfying是成功数组元素类型为B。注意顺序是失败在前、成功在后与直觉上先列成功相反写代码时需留意。同时它提供>export const partition: { A, B, E, R( f: (a: A, i: number) Effect.EffectB, E, R, options?: { readonly concurrency?: Concurrency | undefined } ): (elements: IterableA) Effect.Effect[excluded: ArrayE, satisfying: ArrayB], never, R A, B, E, R( elements: IterableA, f: (a: A, i: number) Effect.EffectB, E, R, options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect[excluded: ArrayE, satisfying: ArrayB], never, R } dual( (args) isIterable(args[0]) !isEffect(args[0]), A, B, E, R( elements: IterableA, f: (a: A, i: number) Effect.EffectB, E, R, options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect[excluded: ArrayE, satisfying: ArrayB], never, R map( forEach(elements, (a, i) result(f(a, i)), options), (results) Arr.partition(results, identity) ) )关键调用链分三步result(f(a, i))对每个元素的效果先用Effect.result包裹。result会把可能失败的EffectB, E, R转换为永不失败的EffectExitB, E, never, R——错误不再是控制流中的失败而是被物化为Exit.fail值进入输出数组。这是永不失败语义的根基失败在第一步就被吸收进返回值而不是向上传播forEach(elements, ..., options)顺序执行或按concurrency并行执行全部元素收集出ArrayExitB, E。由于每个子效果都已被result中和forEach聚合结果也必然是成功的从而保证了外层错误通道为neverArr.partition(results, identity)对Exit数组做同步分区把Exit.fail错误值与Exit.succeed成功值拆成两个数组得到最终的[excluded, satisfying]元组。从源码结构看dual的第一个参数(args) isIterable(args[0]) !isEffect(args[0])决定了>import { Effect } from effect const program Effect.partition([0, 1, 2, 3], (n) n % 2 0 ? Effect.fail(${n} is even) : Effect.succeed(n) ) await Effect.runPromise(program) // [[0 is even, 2 is even], [1, 3]]该示例同时验证了 changeset 描述的两点语义失败被累积而非传播两个偶数元素各自失败错误字符串0 is even、2 is even被收集进第一个元组元素整体永不失败Effect.runPromise直接返回成功值无需处理错误分支。文档还明确了两处行为边界引自 JSDoc 原文The returned tuple is[excluded, satisfying]失败在前、成功在后与 This function runs every effect and never fails每个效果都会被执行整体永不失败——后者说明partition不会像某些组合器那样在首个失败处短路这与Effect.all的默认 fail-fast 行为形成对比all的文档示例中明确标注了 Wont execute due to earlier failure见 Effect.ts。变更的意义与验证方式这条 changeset 的实质影响面可以概括为面向调用方的行为一致性升级自 Effect v3 的用户partition的错误处理代码即不处理错误、只消费元组的写法在 v4 中保持成立不需要按 v3 迁移指南重写该部分逻辑类型层面的可验证性返回类型中never错误通道是编译期契约若实现回归到可能失败使用Effect.runPromise等成功执行器的代码会直接出现类型错误这类偏离在 CI 的类型检查阶段即可被捕获文档即测试示例代码带import.meta.vitest标记配合仓库根目录的 vitest.config.ts 配置文档中的行为断言会作为文档测试参与执行行为变更与文档描述不一致时测试会失败。适用前提说明以上分析基于 effect-smol 仓库当前快照.repos/effect-smolpartition标记since 2.0.0即该 API 自 2.0 起存在本次 changeset 是在后续预发布迭代中修正其偏离 v3 语义的行为若你使用的effect包版本早于本次变更发布行为可能仍是修正前的实现。【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考