fp-ts 丛谈之 Reader 详解
2023-11-12 05:13:23
Reader 概述
Reader 在 fp-ts 中,Reader 的定义如下:
export interface Reader<E, A> {
(e: E): A;
}
也就是一个类型为 r -> a
的函数,r
可以看作计算所需的环境,而 a
是计算的结果。它经常被用来做依赖注入。
先来看一段代码:
const ask = <E, A>(e: E) => () => e;
// 使用场景一
const env = { user: "Alice" };
// 获取当前环境中的用户变量
const getUser = ask<typeof env, string>();
console.log(getUser(env)); // "Alice"
在这个例子中,ask
函数创建了一个 Reader,它将环境作为参数,并返回环境中的 user
变量。然后,我们使用 getUser
Reader 来获取当前环境中的用户变量。
Reader 的使用场景
Reader 经常被用来做依赖注入。依赖注入是一种设计模式,它允许我们通过参数的形式将依赖关系注入到对象中。这使得我们可以很容易地测试对象,而不用担心它是否能够访问它的依赖关系。
例如,我们有一个函数 foo
,它需要一个 UserService
对象作为参数:
function foo(userService: UserService): void {
// ...
}
如果我们想测试 foo
函数,我们就需要创建一个 UserService
对象并把它传递给 foo
函数。这可能会很麻烦,尤其是当 UserService
对象需要很多依赖关系的时候。
我们可以使用 Reader 来解决这个问题。我们可以创建一个 UserServiceReader
Reader,它将 UserService
对象作为参数,并返回一个 foo
函数:
const UserServiceReader = (userService: UserService) => () => foo(userService);
然后,我们就可以使用 UserServiceReader
Reader 来测试 foo
函数:
const userService = new UserService();
const fooReader = UserServiceReader(userService);
fooReader(); // ...
这样,我们就不需要创建 UserService
对象并把它传递给 foo
函数了。我们只需要创建一个 UserServiceReader
Reader,然后使用它来调用 foo
函数即可。
Reader 的组合
Reader 可以组合在一起形成新的 Reader。这可以通过使用 chain
方法来实现。chain
方法接受一个函数作为参数,该函数返回一个新的 Reader。chain
方法将把当前 Reader 的结果作为参数传递给新 Reader。
例如,我们有一个 UserServiceReader
Reader,它将 UserService
对象作为参数,并返回一个 foo
函数:
const UserServiceReader = (userService: UserService) => () => foo(userService);
我们还可以创建一个 UserRepositoryReader
Reader,它将 UserRepository
对象作为参数,并返回一个 getUser
函数:
const UserRepositoryReader = (userRepository: UserRepository) => () => getUser(userRepository);
我们可以使用 chain
方法将这两个 Reader 组合在一起,创建一个新的 Reader:
const combinedReader = UserServiceReader.chain(UserRepositoryReader);
这个新的 Reader 将 UserService
对象和 UserRepository
对象作为参数,并返回一个 foo
函数和一个 getUser
函数。
我们可以使用 combinedReader
Reader 来测试 foo
函数和 getUser
函数:
const userService = new UserService();
const userRepository = new UserRepository();
const combinedReader = UserServiceReader(userService).chain(UserRepositoryReader(userRepository));
const foo = combinedReader();
const user = foo.getUser();
这样,我们就不需要创建 UserService
对象和 UserRepository
对象并把它们传递给 foo
函数和 getUser
函数了。我们只需要创建一个 combinedReader
Reader,然后使用它来调用 foo
函数和 getUser
函数即可。
结语
Reader 是 fp-ts 中一个非常有用的工具。它可以用来做依赖注入,也可以用来组合不同的函数。这使得我们可以编写出更灵活、更易于测试的代码。