Skip to content

为什么选择 Effect?

编程是具有挑战性的。当我们构建库和应用程序时,我们寻求许多工具来处理复杂性并使我们的日常工作更易于管理。Effect 提出了一种在 TypeScript 中思考编程的新方式。

Effect 是一个工具生态系统,帮助您构建更好的应用程序和库。因此,您还将更多地了解 TypeScript 语言以及如何使用类型系统使您的程序更可靠、更易于维护。

在”典型的” TypeScript 中,没有 Effect,我们编写的代码假设函数要么成功要么抛出异常。例如:

const
const divide: (a: number, b: number) => number
divide
= (
a: number
a
: number,
b: number
b
: number): number => {
if (
b: number
b
=== 0) {
throw new
var Error: ErrorConstructor
new (message?: string) => Error
Error
("Cannot divide by zero")
}
return
a: number
a
/
b: number
b
}

基于类型,我们无法知道这个函数可能会抛出异常。我们只能通过阅读代码来发现。当您的代码库中只有一个函数时,这可能看起来不是什么大问题,但当您有数百或数千个函数时,问题就开始累积了。很容易忘记函数可能抛出异常,也很容易忘记处理该异常。

通常,我们会做”最简单”的事情,只是将函数包装在 try/catch 块中。这是防止程序崩溃的良好第一步,但它并不能使我们的复杂应用程序/库更易于管理或理解。我们可以做得更好。

在 TypeScript 中,我们拥有的最重要工具之一是编译器。它是抵御错误、领域错误和一般复杂性的第一道防线。

虽然 Effect 是一个包含许多不同工具的庞大生态系统,但如果必须将其归结为一个想法,那就是:

Effect 的主要独特见解是我们可以使用类型系统来跟踪错误上下文,而不仅仅是如上面除法示例所示的成功值。

这是上面相同的除法函数,但使用了 Effect 模式:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
const
const divide: (a: number, b: number) => Effect.Effect<number, Error, never>
divide
= (
a: number
a
: number,
b: number
b
: number
):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number,
interface Error
Error
, never> =>
b: number
b
=== 0
?
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <Error>(error: Error) => Effect.Effect<never, Error, never>

Creates an Effect that represents a recoverable error.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

Example (Creating a Failed Effect)

import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@seesucceed to create an effect that represents a successful value.

@since2.0.0

fail
(new
var Error: ErrorConstructor
new (message?: string) => Error
Error
("Cannot divide by zero"))
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const succeed: <number>(value: number) => Effect.Effect<number, never, never>

Creates an Effect that always succeeds with a given value.

When to Use

Use this function when you need an effect that completes successfully with a specific value without any errors or external dependencies.

Example (Creating a Successful Effect)

import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)

@seefail to create an effect that represents a failure.

@since2.0.0

succeed
(
a: number
a
/
b: number
b
)

使用这种方法,函数不再抛出异常。相反,错误被作为值处理,可以像成功值一样传递。类型签名也清楚地表明:

  • 函数返回什么成功值(number)。
  • 可能发生什么错误(Error)。
  • 需要什么额外的上下文或依赖(never 表示无)。
┌─── 产生 number 类型的值
│ ┌─── 以 Error 失败
│ │ ┌─── 不需要依赖
▼ ▼ ▼
Effect<number, Error, never>

此外,跟踪上下文允许您为函数提供额外信息,而无需将所有内容作为参数传递。例如,您可以在测试期间将实时外部服务的实现替换为模拟,而无需更改任何核心业务逻辑。

TypeScript 中的应用程序代码经常一遍又一遍地解决相同的问题。与外部服务、文件系统、数据库等交互是所有应用程序开发者的常见问题。Effect 提供了一个丰富的库生态系统,为许多这些问题提供标准化解决方案。您可以使用这些库来构建您的应用程序,或者您可以使用它们来构建您自己的库。

使用 Effect 可以管理错误处理、调试、跟踪、async/promises、重试、流、并发、缓存、资源管理等挑战。您不必重新发明这些问题的解决方案,也不必安装大量依赖项。Effect 在一个伞下解决了许多问题,而这些问题通常需要您安装许多具有不同 API 的不同依赖项来解决。

Effect 深受其他语言(如 Scala 和 Haskell)中出色工作的启发。但是,重要的是要理解 Effect 的目标是成为一个实用的工具包,它竭尽全力解决开发者在 TypeScript 中构建应用程序和库时面临的真实、日常问题。

学习 Effect 非常有趣。Effect 生态系统中的许多开发者正在使用 Effect 解决日常工作中的实际问题,同时也在尝试前沿想法,推动 TypeScript 成为最有用的语言。

您不必一次使用 Effect 的所有方面,可以从生态系统中对您正在解决的问题最有意义的部分开始。Effect 是一个工具包,您可以挑选对您的用例最有意义的部分。但是,随着您的代码库越来越多地使用 Effect,您可能会发现自己想要利用生态系统的更多部分!

Effect 的概念对您来说可能是新的,一开始可能不完全理解。这是完全正常的。花时间阅读文档并尝试理解核心概念 - 当您深入 Effect 生态系统中更高级的工具时,这将真正得到回报。Effect 社区总是乐于帮助您学习和成长。欢迎加入我们的 Discord 或在 GitHub 上讨论!我们欢迎反馈和贡献,并始终在寻找改进 Effect 的方法。