Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ All notable changes to this project will be documented in this file.
### Added

- `TaskEither<E, T>` - a lazy async computation that wraps `() => Promise<Either<E, T>>`, with `map`, `mapLeft`, `flatMap`, `flatten`, `ap`, `zip`, `tap`, `tapLeft`, `match`, `getOrElse`, `orElse`, and `run`
- Full `TaskEither` documentation
- `TaskEither` section added to the Getting Started page

## [0.2.1](https://github.com/pwlmc/okfp/compare/v0.2.0...v0.2.1) - 2026-02-25

Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ OK-FP is a small, focused functional programming toolkit for TypeScript.

It provides a minimal set of **typed effects**: composable, type-safe wrappers for optional values, errors, and async computations.

## Status

OK-FP is pre-1.0.

- ✅ Implemented: `Option`, `Either`, `Validation`, `Task`
- 🚧 Planned before `v1.0.0`: `TaskEither`

See: [ROADMAP.md](./ROADMAP.md)
## Effects

| Effect | Description |
|---|---|
| [`Option<T>`](./docs/option.md) | A value that may or may not be present. Use instead of `null`/`undefined`. |
| [`Either<E, T>`](./docs/either.md) | A computation that succeeds with `T` or fails with a typed error `E`. Errors are explicit and must be handled. |
| [`Validation<E, T>`](./docs/validation.md) | Like `Either`, but accumulates **all** errors instead of stopping at the first one. Ideal for form and config validation. |
| [`Task<T>`](./docs/task.md) | A lazy async computation that always succeeds. Executes only when `.run()` is called — unlike Promises, which are eager. |
| [`TaskEither<E, T>`](./docs/task-either.md) | A lazy async computation that can succeed with `T` or fail with `E`. Combines `Task`'s laziness with `Either`'s typed errors. |

## Installation

Expand Down
1 change: 1 addition & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
- [x] (feat) Validation (applicative)
- [x] (feat) Task
- [x] (feat) TaskEither
- [x] (doc) TaskEither documentation
- [ ] (doc) Document design decisions
- [ ] (doc) Comprehensive FP effects guide
37 changes: 37 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,40 @@ const [user1, user2] = await all([
:::

**Dive deeper into `Task`:** See the [Task guide](./task.md) for all available methods and patterns.

## Fallible Async Computations: TaskEither

When your async operation can fail with a typed error, use `TaskEither`. It is a lazy `() => Promise<Either<E, T>>` — combining `Task`'s laziness with `Either`'s typed error handling.

```ts
import { tryCatch, taskEither, all } from "ok-fp/taskEither";

type User = { id: string; name: string };

const fetchUser = (id: string) =>
tryCatch(
() => fetch(`/api/users/${id}`).then((r) => r.json() as Promise<User>),
(err) => `Fetch failed: ${err}`,
);

// Chain two async steps — short-circuits on first error
const greeting = fetchUser("a-001")
.map((user) => `Hello, ${user.name}!`);

const result = await greeting.run();
result.match(
(err) => console.error(err),
(msg) => console.log(msg), // "Hello, Alice!"
);

// Run multiple requests concurrently
const [user1, user2] = await all([fetchUser("a-001"), fetchUser("b-002")])
.getOrElse(() => [])
.run();
```

::: tip Key takeaway
`TaskEither` makes the error type visible in the signature and forces you to handle it. Use it for any async operation that can fail — API requests, file reads, database queries.
:::

**Dive deeper into `TaskEither`:** See the [TaskEither guide](./task-either.md) for all available methods and patterns.
Loading