1. Effect 是什么?
很多 JavaScript 开发者刚接触 Effect 时,会产生一个疑问:
Effect 是 Node.js 吗?是 TypeScript 的一部分吗?
答案:
都不是。
Effect 是一个独立的 TypeScript/JavaScript 第三方库。
安装:
npm install effect
使用:
import { Effect } from "effect"
它运行在:
Node.js / Bun
|
|
↓
Effect
关系:
| 技术 | 作用 |
|---|---|
| Node.js | JavaScript 运行环境 |
| Bun | JavaScript 运行环境 |
| TypeScript | 编程语言扩展 |
| Effect | 管理复杂程序逻辑的库 |
2. 为什么需要 Effect?
2.1 普通 JavaScript 的问题
假设实现一个用户登录:
async function login(username, password) {
const user = await getUser(username)
const result = await checkPassword(
user,
password
)
return result
}
看起来很简单。
但是现实中:
login
|
|
+---- 查询数据库
|
+---- 验证密码
|
+---- 调用第三方服务
|
+---- 写入日志
每一步都有可能失败:
- 数据库连接失败
- 用户不存在
- 密码错误
- 网络超时
- API 服务异常
于是代码变成:
async function login(username,password){
try {
const user =
await getUser(username)
const result =
await checkPassword(
user,
password
)
return result
} catch(error){
console.log(error)
}
}
当项目变大:
用户登录
↓
订单系统
↓
支付系统
↓
消息系统
↓
AI Agent
错误处理、状态管理、依赖管理会越来越复杂。
Effect 就是为了解决这些问题。
3. Effect 的核心思想
普通 JavaScript
函数执行:
调用函数
↓
立即执行
↓
返回结果
例如:
console.log("hello")
立即打印:
hello
Effect
Effect 的思想:
不立即执行,而是先描述一个任务。
例如:
const task =
Effect.sync(() => {
console.log("hello")
})
这里:
没有打印。
只是创建了一个任务:
未来执行:
打印 hello
真正运行:
Effect.runSync(task)
才会输出:
hello
4. Effect 的三个核心泛型
Effect 最常见形式:
Effect<A,E,R>
三个参数:
Effect<
成功结果,
错误类型,
运行环境
>
例如:
Effect<User, LoginError, Database>
表示:
这个任务:
成功:
返回 User
失败:
返回 LoginError
运行需要:
Database
4.1 A:Success Value
A 表示成功结果。
例如:
Effect<User,Error,Database>
成功:
User
4.2 E:Error
E 表示错误。
例如:
Effect<User,UserNotFoundError,Database>
可能失败:
UserNotFoundError
4.3 R:Requirements
R 表示依赖。
例如:
Effect<User,Error,Database>
说明:
运行这个任务需要:
Database
5. Effect 基础 API
学习 Effect,第一阶段需要掌握:
Effect.succeed()
Effect.fail()
Effect.runPromise()
6. Effect.succeed()
作用
创建一个成功任务。
示例:
const task =
Effect.succeed(100)
表示:
执行任务
↓
成功返回
100
类型:
Effect<number,never,never>
含义:
成功:
number
失败:
不会失败
依赖:
没有
7. Effect.fail()
作用
创建失败任务。
示例:
const error =
Effect.fail(
"用户不存在"
)
表示:
执行任务
↓
失败
↓
返回错误
类型:
Effect<never,string,never>
为什么不用 throw?
传统:
throw new Error(
"密码错误"
)
问题:
调用者不知道:
这个函数会不会抛异常?
Effect:
Effect.fail(
"密码错误"
)
错误直接成为类型的一部分:
Effect<User,PasswordError,never>
开发者一眼知道:
可能失败。
8. Effect.runPromise()
前面说:
Effect 只是描述任务。
例如:
const task =
Effect.succeed(100)
此时:
task
↓
任务描述
还没有执行。
执行:
Effect.runPromise(task)
转换:
Effect
↓
Promise
例如:
Effect.runPromise(task)
.then(value=>{
console.log(value)
})
输出:
100
9. Effect.gen:组合复杂流程
真实项目不会只有一个任务。
例如 AI Agent:
用户需求
↓
读取文件
↓
调用 GPT
↓
修改代码
↓
执行测试
↓
返回结果
普通 async:
async function agent(){
const file =
await readFile()
const answer =
await callGPT(file)
const result =
await save(answer)
return result
}
Effect:
const agent =
Effect.gen(function*(){
const file =
yield* readFile()
const answer =
yield* callGPT(file)
const result =
yield* save(answer)
return result
})
其中:
yield*
类似:
await
但是 Effect 额外知道:
- 哪一步失败
- 需要什么依赖
- 如何恢复
10. Error Handling(错误处理)
Effect 最大特点:
Error as Value
错误也是数据。
传统:
throw error
流程:
正常流程
↓
异常跳出
↓
catch
Effect:
错误属于返回类型:
Effect<
Result,
AgentError,
Environment
>
例如:
Effect<
string,
FileError | GPTError,
never
>
表示:
可能失败:
FileError
或者
GPTError
处理:
Effect.catchAll(
task,
error => {
return Effect.succeed(
"处理失败"
)
}
)
11. Layer:依赖注入系统
大型项目最大的问题:
代码耦合。
例如:
function saveUser(){
mysql.insert()
}
写死:
只能 MySQL
以后换:
PostgreSQL
SQLite
Mock数据库
怎么办?
Effect 使用依赖注入。
思想:
业务代码
↓
只声明需要 Database
↓
运行时提供具体实现
结构:
Agent
↓
Database Service
↓
Layer
↓
SQLite / MySQL
例如:
开发环境:
Mock Database
生产环境:
PostgreSQL
业务代码不用修改。
12. Service:大型项目模块化
AI Agent 通常需要:
GPT Service
File Service
Terminal Service
Memory Service
不要:
import openai
import fs
import child_process
到处调用。
而是:
定义服务:
GPTService
FileService
TerminalService
Agent:
只依赖接口。
结构:
Agent
↓
Service接口
↓
具体实现
↓
GPT / Claude / Local LLM
好处:
以后:
GPT 换 Claude:
不用修改 Agent。
13. Schema:数据验证
外部数据永远不可信。
例如:
用户输入:
{
"name":"Tom",
"age":"20"
}
问题:
age 是字符串。
程序需要:
{
"name":"Tom",
"age":20
}
Schema 可以:
- 验证数据
- 转换数据
- 保证类型安全
例如:
const User =
Schema.Struct({
name: Schema.String,
age: Schema.Number
})
要求:
{
"name":"Tom",
"age":20
}
14. Effect 学习路线总结
推荐顺序:
第一阶段:任务基础
Effect
|
+-- succeed
|
+-- fail
|
+-- runPromise
第二阶段:任务组合
Effect.gen
第三阶段:可靠性
Error Handling
|
+-- Retry
|
+-- Timeout
|
+-- Logging
第四阶段:大型架构
Service
Layer
第五阶段:数据安全
Schema
15. Effect 在 AI Agent 中的位置
以 Claude Desktop 类应用为例:
Desktop App
|
|
↓
Agent Core
|
|
-------------------
| | |
GPT File Terminal
Service Service Service
|
|
Layer
|
|
Database / OS / API
Effect 负责:
| 功能 | Effect模块 |
|---|---|
| 任务流程控制 | Effect.gen |
| GPT调用失败处理 | Error Handling |
| 文件系统管理 | Service |
| Terminal执行 | Service |
| 切换模型 | Layer |
| 数据验证 | Schema |
| 依赖管理 | Layer + Service |
总结
如果把普通 JavaScript 比作:
可以写程序的工具
那么 Effect 就像:
一个大型软件工程管理系统。
它让程序拥有:
- 明确的成功结果
- 明确的错误类型
- 可替换的依赖
- 可组合的任务
- 可维护的大型架构
对于 AI Agent 开发:
Bun
↓
运行环境
SolidJS
↓
界面
Effect
↓
Agent任务执行引擎
AI SDK
↓
GPT / Claude
SQLite
↓
数据存储
其中 Effect 是连接所有能力的核心业务逻辑层。