发布于 2026-01-06 3 阅读
0

FP-TS P1实用指南:管道和流量

FP-TS P1实用指南:管道和流量

介绍

本文将介绍fp-ts,一个用于 TypeScript 的函数式编程库。为什么要学习 fp-ts 呢?首先,它能提供更好的类型安全性。fp-ts 允许你对数据结构进行断言,而无需编写用户自定义的类型守卫或使用 ` asif` 运算符。其次,它能增强表达力和可读性。fp-ts 为你提供了优雅地建模一系列可能失败的操作所需的工具。总而言之,你应该将 fp-ts 添加到你的工具库中,因为它能帮助你编写更优秀的 TypeScript 程序。

与普遍认知相反,学习函数式编程并不需要理解复杂的数学原理。实际上,你只需要熟悉每个运算符的运作方式即可。一旦掌握了基本运算符,你就可以回头复习相关的数学知识。基于此,本文及后续文章将从实践角度介绍函数式编程,尽量避免使用数学术语。

管道操作员

fp-ts 的基本构建块是管道运算符。直观地说,你可以使用该运算符从左到右链接一系列函数。管道运算符的类型定义pipe允许接受任意数量的参数。第一个参数可以是任意值,后续参数必须是参数个数为1的函数。管道中前一个函数的返回类型必须与后一个函数的输入类型匹配。

我们来看看如何通过管道传递简单的加法和乘法函数。

import { pipe } from 'fp-ts/lib/function'

function add1(num: number): number {
 return num + 1
}

function multiply2(num: number): number {
 return num * 2
}

pipe(1, add1, multiply2) // 4
Enter fullscreen mode Exit fullscreen mode

这次运算的结果是4。我们是如何得出这个结果的呢?让我们来看看步骤。

  1. 我们从该值开始1
  2. 1被传递给第一个参数add1,并通过添加add1来求值21
  3. add1返回值2被传递给第一个参数,并通过乘以multiply2来计算42

目前,我们的管道输入一个数字,输出一个新的数字。是否可以将输入类型转换为另一种类型,例如字符串string?答案是肯定的。让我们toString在管道的末端添加一个函数。

function toString(num: number): string {
 return `${num}`
}

pipe(1, add1, multiply2, toString) // '4'
Enter fullscreen mode Exit fullscreen mode

现在我们的管道计算结果为。如果我们在之间’4’插入 会发生什么?我们会得到一个编译错误因为 的输出类型与输入类型不匹配toStringadd1multiply2toStringstringmultiply2number

pipe(1, add1, toString, multiply2)
Enter fullscreen mode Exit fullscreen mode

类型为“(num: number) => string”的参数不能赋值给类型为“(b: number) => number”的参数。
类型“string”不能赋值给类型“number”。ts (2345)

简而言之,您可以使用该pipe运算符通过一系列函数来转换任何值。控制流程可以建模如下。1

A -> (A->B) -> (B->C) -> (C->D)
Enter fullscreen mode Exit fullscreen mode

流量算子

flow运算符几乎与另一个运算符类似pipe。区别在于,第一个参数必须是一个函数,而不是任意值(例如数字)。此外,第一个函数的参数个数可以大于 1。

例如,我们可以将这三个函数包装在flow运算符中。

import { flow } from 'fp-ts/lib/function'

pipe(1, flow(add1, multiply2, toString))
flow(add1, multiply2, toString)(1) // this is equivalent
Enter fullscreen mode Exit fullscreen mode

相比之下,对于操作pipe员而言,这就是操作员所看到的控制“流程” flow。2

(A->B) -> (B->C) -> (C->D) -> (D->E)
Enter fullscreen mode Exit fullscreen mode

`into` 运算符的最佳使用场景是什么flow?何时应该使用它而不是 ` pipeinto` 运算符?一般来说,当您想要避免使用匿名函数时,就应该使用它。在 TypeScript 中,回调函数就是一个很好的匿名函数示例。

我们声明一个函数,concat其参数个数为 n 2。第一个参数是一个数字。第二个参数是一个回调函数,它接受一个数字作为第一个参数,并将其转换为字符串。该函数返回第一个值和转换后的第二个值,以二维元组的形式返回。

function concat(a: number, transformer: (a: number) => string): [number, string] {
  return [a, transformer(a)]
}
Enter fullscreen mode Exit fullscreen mode

利用我们之前掌握的函数,我们可以为这个函数编写一个回调函数。下面是一个使用该pipe运算符的示例。

concat(1, (n) => pipe(n, add1, multiply2, toString)) // [1, '4']
Enter fullscreen mode Exit fullscreen mode

这样做有什么问题?问题在于,我们需要将其声明n为匿名函数的一部分才能与该pipe运算符一起使用。你应该避免这样做,因为它会让你面临覆盖外部作用域变量的风险。此外,这样做也更冗长。

解决方法是使用flow运算符并移除匿名函数。这样做之所以有效,是因为该运算符的返回值flow本身就是一个函数。该函数的签名number -> string与回调函数的签名完全相同。

concat(1, flow(add1, multiply2, toString)) // [1, '4']
Enter fullscreen mode Exit fullscreen mode

  1. 管道 TypeScript 定义 

  2. Flow TypeScript 定义 

文章来源:https://dev.to/ryanleecode/practical-guide-to-fp-ts-pipe-and-flow-4e9n