Effect - typed effects, error handling và concurrency
Effect là thư viện TypeScript cho functional programming, xử lý lỗi có kiểu, quản lý dependency và concurrency — thay try/catch và Promise.
Effect (effect.website) là một thư viện TypeScript mạnh mẽ, mang typed effects, error handling có cấu trúc, quản lý dependency và concurrency vào hệ sinh thái TypeScript. Không giống như các thư viện functional programming khác, Effect được thiết kế để thay thế hoàn toàn try/catch, Promise, và dependency injection truyền thống bằng một hệ thống thống nhất, type-safe.
Vấn đề với TypeScript thuần#
TypeScript không có cơ chế native để:
| Vấn đề | Cách xử lý hiện tại | Hạn chế |
|---|---|---|
| Lỗi có kiểu | throw new Error() | Không biết kiểu lỗi ở call site |
| Dependency injection | Constructor injection, DI container | Không type-safe, runtime overhead |
| Tác vụ bất đồng bộ | Promise, async/await | Không cancel được, không compose |
| Tác vụ song song | Promise.all() | Lỗi một → hỏng tất cả |
| Resource management | try/finally | Dễ quên cleanup |
Effect giải quyết tất cả những vấn đề này trong một framework duy nhất.
Core Concepts#
1. Effect — Đơn vị cơ bản#
Effect là một mô tả type-safe của một tác vụ có thể thất bại. Bạn không chạy code ngay lập tức — bạn xây dựng một pipeline, sau đó run nó:
import { Effect } from 'effect'
// Một effect thành công (never fails)
const success: Effect.Effect<number, never, never> = Effect.succeed(42)
// Một effect có thể thất bại
const fail: Effect.Effect<number, string, never> = Effect.fail('Something went wrong')
// Run effect
const result = await Effect.runPromise(success)
console.log(result) // 42tsChữ ký type Effect<Success, Error, Requirements> cho bạn biết:
- Success: kiểu trả về khi thành công
- Error: kiểu lỗi có thể xảy ra
- Requirements: dependencies cần thiết
2. Error Handling có kiểu#
Không còn catch (e: unknown) nữa:
import { Effect } from 'effect'
class NetworkError {
readonly _tag = 'NetworkError'
constructor(readonly status: number) {}
}
class ValidationError {
readonly _tag = 'ValidationError'
constructor(readonly field: string) {}
}
// TypeScript biết chính xác error type
type ApiError = NetworkError | ValidationError
const fetchUser: Effect.Effect<User, ApiError, never> = Effect.gen(function* () {
const response = yield* fetch('/api/user')
if (response.status === 404) {
return yield* Effect.fail(new NetworkError(404))
}
return yield* Effect.succeed(await response.json())
})
// Pattern matching trên errors
const handled = fetchUser.pipe(
Effect.catchTag('NetworkError', (err) =>
Effect.succeed({ id: 0, name: 'fallback' })
),
Effect.catchTag('ValidationError', (err) =>
Effect.fail(`Invalid field: ${err.field}`)
)
)ts3. Dependency Injection type-safe#
Effect quản lý dependencies thông qua Layer và Context — không runtime, không global state:
import { Effect, Layer, Context } from 'effect'
// 1. Định nghĩa service tag
class Database extends Context.Tag('Database')<
Database,
{ query: (sql: string) => Effect.Effect<unknown[], Error, never> }
>() {}
// 2. Implement layer
const DatabaseLive = Layer.succeed(Database, {
query: (sql) => Effect.succeed([{ id: 1, name: 'test' }])
})
// 3. Sử dụng — Effect biết cần Database
const program = Database.pipe(
Effect.flatMap((db) => db.query('SELECT * FROM users'))
)
// Type: Effect<unknown[], Error, Database> ⬅️ cần Database
// 4. Inject dependency
const runnable = Effect.provide(program, DatabaseLive)
const result = await Effect.runPromise(runnable)ts4. Concurrency & Fibers#
Effect có hệ thống Fiber riêng — lightweight threads có thể cancel, fork, và join:
import { Effect, Fiber } from 'effect'
// Fork — chạy song song
const task1 = Effect.succeed('Task 1').pipe(Effect.delay('1 second'))
const task2 = Effect.succeed('Task 2').pipe(Effect.delay('2 seconds'))
// Race — lấy kết quả nhanh nhất
const winner = Effect.raceAll([task1, task2])
// Fork/Join — chạy song song, đợi tất cả
const fiber = yield* Effect.fork(task1)
const result = yield* Fiber.join(fiber)
// Timeout — tự động fail nếu quá lâu
const withTimeout = task1.pipe(Effect.timeout('500 millis'))ts5. Scoped Resources#
Quản lý resource tự động — không lo quên close():
import { Effect, Scope } from 'effect'
const openFile = (path: string): Effect.Effect<FileHandle, Error, Scope> =>
Effect.acquireRelease(
Effect.sync(() => fs.openSync(path)), // acquire
(file) => Effect.sync(() => fs.closeSync(file)) // release
)
// Scope tự động close khi kết thúc
const content = yield* Scope.use(
Effect.scope,
(scope) => openFile('data.txt').pipe(
Effect.flatMap((file) => readFileContent(file))
)
)
// File đã được close tự độngts6. Streams#
Stream là phiên bản có kiểu của Observable/AsyncIterable:
import { Stream, Sink, Effect } from 'effect'
const numbers = Stream.iterate(0, (n) => n + 1).pipe(
Stream.take(5),
Stream.map((n) => n * 2)
)
// Collect thành array
const result = await Effect.runPromise(
Stream.runCollect(numbers)
)
// [0, 2, 4, 6, 8]tsEcosystem#
Effect có một hệ sinh thái đang phát triển nhanh:
| Package | Mục đích |
|---|---|
effect/Schema | Schema validation & marshalling (thay thế Zod) |
effect/Platform | File system, path, streams platform APIs |
@effect/sql | SQL database access (Postgres, SQLite, MySQL) |
@effect/rpc | RPC framework với type-safe protocols |
@effect/cluster | Distributed computing cluster |
@effect/opentelemetry | OpenTelemetry observability |
effect/Cron | Cron job scheduling |
Effect vs các giải pháp khác#
| Tiêu chí | try/catch | Promise | Zod | fp-ts | Effect |
|---|---|---|---|---|---|
| Error type-safe | ❌ | ❌ | ❌ | ✅ | ✅ |
| Dependency injection | ❌ | ❌ | ❌ | ❌ | ✅ |
| Cancellation | ❌ | ❌ | ❌ | ❌ | ✅ |
| Resource safety | ❌ | ❌ | ❌ | ❌ | ✅ |
| Concurrency | ❌ | ❌ | ❌ | ❌ | ✅ |
| Streaming | ❌ | ❌ | ❌ | ❌ | ✅ |
| Schema validation | ❌ | ❌ | ✅ | ❌ | ✅ |
Effect trong thực tế#
Alchemy — Infrastructure as Code#
Alchemy (alchemy.run) sử dụng Effect làm nền tảng để định nghĩa và quản lý cloud infrastructure. Effect cho phép xử lý lỗi mạng, retry, và resource lifecycle một cách an toàn:
import { Effect } from 'effect'
// Effect xử lý retry khi API call thất bại
const provision = Effect.retry(
cloudflare.createBucket('my-bucket'),
{ times: 3, delay: '1 second' }
)tsWeb Framework — effect-http#
import { HttpServer, HttpRouter, HttpMiddleware } from '@effect/platform'
import { BunHttpServer } from '@effect/platform-bun'
const app = HttpRouter.empty.pipe(
HttpRouter.get('/',
Effect.succeed(new Response('Hello, Effect!'))
),
HttpRouter.get('/users/:id',
(req) => fetchUser(req.params.id).pipe(
Effect.map((user) => Response.json(user)),
Effect.catchTag('NetworkError', () =>
Effect.succeed(new Response('Not found', { status: 404 }))
)
)
)
)
BunHttpServer.serve(app).pipe(
Effect.runPromise
)tsBắt đầu với Effect#
# Tạo project
mkdir my-effect-app && cd my-effect-app
npm init -y
npm install effect @effect/platform @effect/platform-bunshellimport { Effect, Console } from 'effect'
const program = Effect.gen(function* () {
const name = yield* Effect.succeed('World')
yield* Console.log(`Hello, ${name}!`)
return name
})
Effect.runPromise(program).then(console.log)tsKết luận#
Effect không chỉ là một thư viện — nó là một cách tiếp cận mới để viết TypeScript. Với hệ thống kiểu mạnh mẽ, error handling có cấu trúc, và quản lý dependency type-safe, Effect giúp bạn viết code đáng tin cậy hơn, dễ maintain hơn, và dễ test hơn.
Nếu bạn muốn đưa TypeScript lên một tầm cao mới, Effect là công cụ đáng đầu tư thời gian nhất hiện nay.
Link: effect.website ↗
GitHub: github.com/Effect-TS/effect ↗
Discord: discord.gg/effect-ts ↗