fp-ts Pipe and Flow Composition 是 fp-ts 中关于函数组合的指南,核心是 pipe 与 flow 两个工具。该文档通过导入示例、概念对比、代码片段和最佳实践,帮助开发者构建类型安全的数据转换管道。
一、用途 函数组合是函数式编程的核心。fp-ts 提供 pipe 和 flow 两种组合方式,本指南围绕二者讲解如何编写优雅、类型安全的管道。文档强调的组合目标包括:可读的自上而下数据流、可复用的转换函数,以及在与 fp-ts 数据类型协作时保持类型推断。
二、主要能力
- pipe:接收一个值,将其依次传入一串函数并立即执行结果。文档给出的等价形式为 pipe(value, fn1, fn2, fn3) 等价于 fn3(fn2(fn1(value)))。适合已有值需要立即转换、一次性转换、处理 fp-ts 数据类型(Option、Either、Task 等)以及需要自上而下数据流的场景。
- flow:将函数组合成一个新函数而不执行。文档给出的等价形式为 flow(fn1, fn2, fn3) 等价于 (x) => fn3(fn2(fn1(x)))。适合创建可复用转换、定义作为回调传递的函数、构建可组合工具,以及尚未拿到输入值的场景。
- 对比:pipe 立即执行、第一个参数是值、返回转换后的值、用于现在转换数据;flow 延迟执行、第一个参数是函数、返回新函数、用于创建可复用的转换。文档同时给出二者等价写法示例。
- 处理不同参数数量:fp-ts 多数操作返回一元函数,便于组合;遇到多参数函数时可用内联箭头函数包装、柯里化版本或 fp-ts-std 的 curry2 等工具。
- 数据转换管道:覆盖数组转换(filter、map、sort、groupBy,结合 NonEmptyArray、Record、string 等模块)、Record/对象转换(R.map、R.filter)与字符串转换(trim、toLowerCase、替换、配合 Option 做校验)。
- 与 fp-ts 数据类型组合:与 Option(fromNullable、flatMap、map 链式取值)、Either(校验、E.Do、E.bind 顺序校验;使用 getApplicativeValidation 与 sequenceS 累积错误)、Task 与 TaskEither(tryCatch 包装异步请求、flatMap 串联依赖、A.sequence 并行执行)、Reader 与 ReaderTaskEither(通过 ask、flatMapTaskEither、tap 注入依赖并串联副作用)。
三、使用方式 文档展示的典型用法包括:从 'fp-ts/function' 导入 pipe、flow、identity,从 'fp-ts/function' 导入 tap;在管道中按顺序放入过滤、映射、排序、格式化等步骤;用 flow 定义可复用的归一化或校验函数,再组合成处理函数;在复杂依赖场景使用 Do 记法与 bind;在需要累积错误时使用 Applicative Validation。该指南也给出类型推断建议:尽量让 TypeScript 推断类型,仅在第一步函数或公共 API 上显式标注。
四、适用场景 文档列出的适用场景包括:一次性数据转换、构建可复用转换管道、把转换函数作为回调传递、构建工具库、处理 fp-ts 的 Option/Either/Task/TaskEither/ReaderTaskEither 类型链,以及组合异步请求、并行获取并聚合并发结果、顺序执行且提前退出、降级回退链、条件分支和结果累积等常见组合模式。
五、限制与注意事项 文档提到 pipe 开销极小,flow 创建新函数有轻微创建开销但执行时没有;热路径上已知值用 pipe 略快,可复用函数用 flow 可避免重复创建。在性能建议中,文档提醒避免不必要的中间数组:多次 filter 可以合并为一次,或使用 filterMap 将过滤与映射合并。调试方面,可用 tap 产生副作用或自定义 trace 辅助函数打印中间值。文档未提供该技能自身的安装命令、收费模式、平台支持或价格信息,也未给出具体的 package 版本要求。
六、最佳实践摘要 文档建议:保持函数小而专注;提取具名函数以表达意图;用 flow 定义可复用转换;按逻辑分组相关操作;在能够有意义回应的层级处理错误;复杂依赖使用 Do 记法;在 public API 上使用显式类型;让 TypeScript 尽可能推断类型。最后以表格总结何时用 pipe、何时用 flow,并强调 pipe(value, f, g) 立即执行、flow(f, g) 返回供后续使用的函数。