已发布 上游基线 bf46254 原文 ↗ 在 GitHub 编辑

匹配

学习如何在 Effect 程序中处理成功与失败的情况,包括模式匹配、忽略值、副作用以及精确的失败分析等工具。

在 Effect 模块中,与其他模块(例如 OptionExit)类似,我们有一个 Effect.match 函数,可以同时处理不同的情况。此外,Effect 还提供了多种函数来管理带 effect 的程序中的成功与失败场景。

match

Effect.match 允许你为成功和失败两种场景分别定义自定义的处理函数。你为每种情况提供一个单独的函数:当该 effect 成功时处理其结果,当它失败时处理其错误。

当你希望代码对成功或失败作出不同响应、又不触发副作用时,这很有用。

示例(同时处理成功与失败的情况)

import { Effect } from "effect"

const success: Effect.Effect<number, Error> = Effect.succeed(42)

const program1 = Effect.match(success, {
  onFailure: (error) => `failure: ${error.message}`,
  onSuccess: (value) => `success: ${value}`,
})

// Run and log the result of the successful effect
Effect.runPromise(program1).then(console.log)
// Output: "success: 42"

const failure: Effect.Effect<number, Error> = Effect.fail(new Error("Uh oh!"))

const program2 = Effect.match(failure, {
  onFailure: (error) => `failure: ${error.message}`,
  onSuccess: (value) => `success: ${value}`,
})

// Run and log the result of the failed effect
Effect.runPromise(program2).then(console.log)
// Output: "failure: Uh oh!"

ignore

Effect.ignore 允许你运行一个 effect,而不关心它的结果——无论它成功还是失败。

当你只关心该 effect 的副作用、不需要处理或加工它的结果时,这很有用。

示例(使用 Effect.ignore 丢弃值)

import { Effect } from "effect"

//      ┌─── Effect<number, string, never>
//      ▼
const task = Effect.fail("Uh oh!").pipe(Effect.as(5))

//      ┌─── Effect<void, never, never>
//      ▼
const program = Effect.ignore(task)

matchEffect

Effect.matchEffect 函数与 Effect.match 类似,但它允许你在处理成功和失败结果的处理函数中执行副作用。

当你需要根据 effect 成功还是失败来执行额外的操作(例如记录日志或通知用户)时,这很有用。

示例(带副作用地处理成功与失败)

import { Effect } from "effect"

const success: Effect.Effect<number, Error> = Effect.succeed(42)
const failure: Effect.Effect<number, Error> = Effect.fail(new Error("Uh oh!"))

const program1 = Effect.matchEffect(success, {
  onFailure: (error) =>
    Effect.succeed(`failure: ${error.message}`).pipe(Effect.tap(Effect.log)),
  onSuccess: (value) =>
    Effect.succeed(`success: ${value}`).pipe(Effect.tap(Effect.log)),
})

console.log(Effect.runSync(program1))
/*
Output:
timestamp=... level=INFO fiber=#0 message="success: 42"
success: 42
*/

const program2 = Effect.matchEffect(failure, {
  onFailure: (error) =>
    Effect.succeed(`failure: ${error.message}`).pipe(Effect.tap(Effect.log)),
  onSuccess: (value) =>
    Effect.succeed(`success: ${value}`).pipe(Effect.tap(Effect.log)),
})

console.log(Effect.runSync(program2))
/*
Output:
timestamp=... level=INFO fiber=#1 message="failure: Uh oh!"
failure: Uh oh!
*/

matchCause

Effect.matchCause 函数允许你在处理失败时访问某个 Fiber 内失败的完整 cause

当你需要区分不同类型的错误(例如常规失败、defect 或中断)时,这很有用。你可以基于 cause 为每种失败类型提供特定的处理逻辑。

示例(处理不同的失败 cause)

import { Effect } from "effect"

const task: Effect.Effect<number, Error> = Effect.die("Uh oh!")

const program = Effect.matchCause(task, {
  onFailure: (cause) => {
    switch (cause._tag) {
      case "Fail":
        // Handle standard failure
        return `Fail: ${cause.error.message}`
      case "Die":
        // Handle defects (unexpected errors)
        return `Die: ${cause.defect}`
      case "Interrupt":
        // Handle interruption
        return `${cause.fiberId} interrupted!`
    }
    // Fallback for other causes
    return "failed due to other causes"
  },
  onSuccess: (value) =>
    // task completes successfully
    `succeeded with ${value} value`,
})

Effect.runPromise(program).then(console.log)
// Output: "Die: Uh oh!"

matchCauseEffect

Effect.matchCauseEffect 函数的工作方式与 Effect.matchCause 类似,但它还允许你基于失败 cause 执行额外的副作用。

该函数提供对失败完整 cause 的访问,从而可以区分各种失败类型,并让你在执行副作用(例如记录日志或其他操作)的同时作出相应的响应。

示例(带副作用地处理不同的失败 cause)

import { Effect, Console } from "effect"

const task: Effect.Effect<number, Error> = Effect.die("Uh oh!")

const program = Effect.matchCauseEffect(task, {
  onFailure: (cause) => {
    switch (cause._tag) {
      case "Fail":
        // Handle standard failure with a logged message
        return Console.log(`Fail: ${cause.error.message}`)
      case "Die":
        // Handle defects (unexpected errors) by logging the defect
        return Console.log(`Die: ${cause.defect}`)
      case "Interrupt":
        // Handle interruption and log the fiberId that was interrupted
        return Console.log(`${cause.fiberId} interrupted!`)
    }
    // Fallback for other causes
    return Console.log("failed due to other causes")
  },
  onSuccess: (value) =>
    // Log success if the task completes successfully
    Console.log(`succeeded with ${value} value`),
})

Effect.runPromise(program)
// Output: "Die: Uh oh!"