-
Notifications
You must be signed in to change notification settings - Fork 0
chore: document api decision #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -138,4 +138,7 @@ dist | |
| vite.config.js.timestamp-* | ||
| vite.config.ts.timestamp-* | ||
|
|
||
| .vscode | ||
| .vscode | ||
|
|
||
| # Agents | ||
| .claude | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # OK-FP | ||
|
|
||
| Essential Effect Data Types for TypeScript. | ||
|
|
||
| ## Project Structure | ||
|
|
||
| Single-package library (not a monorepo): | ||
|
|
||
| - `src/` - Source code, one directory per effect type | ||
| - `docs/` - VitePress documentation site | ||
| - `dist/` - Build output (not committed) | ||
|
|
||
| Each effect type follows the same layout: | ||
|
|
||
| - `src/{type}/` - Implementation directory | ||
| - `src/{type}.ts` - Barrel re-export | ||
| - `src/{type}/model.ts` - Type definitions | ||
| - `src/{type}/constructors.ts` - Factory functions | ||
| - `src/{type}/helpers.ts` - Utility functions | ||
| - `src/{type}/{type}.ts` - Core implementation | ||
| - `src/{type}/{type}.spec.ts` - Tests | ||
| - `src/{type}/constructors.spec.ts` - Constructor tests | ||
| - `src/{type}/helpers.spec.ts` - Helper tests | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - **Runtime:** Node.js (ESM) | ||
| - **Language:** TypeScript (strict mode) | ||
| - **Build:** tsdown | ||
| - **Test:** vitest (with `@vitest/coverage-v8`) | ||
| - **Lint:** Biome | ||
| - **Docs:** VitePress | ||
|
|
||
| ## Commands | ||
|
|
||
| ```bash | ||
| npm run lint # Run Biome check | ||
| npm run typecheck # Run tsc --noEmit | ||
| npm run test # Run vitest | ||
| npm run build # Build with tsdown | ||
| npm run docs:dev # Dev server for docs | ||
| npm run docs:build # Build docs | ||
| ``` | ||
|
|
||
| ## Key Conventions | ||
|
|
||
| - Tests are co-located with source files using `.spec.ts` suffix | ||
| - Imports use `.js` extensions (ESM convention for TypeScript) | ||
| - `clearMocks: true` is set globally in vitest config. No need to manually reset mocks. | ||
| - Each effect type has its own barrel export in `src/{type}.ts` | ||
| - Package exports are per-effect: `ok-fp/option`, `ok-fp/either`, `ok-fp/validation`, `ok-fp/task`, `ok-fp/taskEither` | ||
| - Test utilities for algebraic laws (functor, monad, applicative) live in `src/testUtils/` | ||
|
|
||
| ## Agent Guidelines | ||
|
|
||
| ### Permissions | ||
|
|
||
| - **Allowed without asking:** non-mutating npm scripts (`lint`, `typecheck`, `test`, `build`), reading project files, creating commits. | ||
| - **Requires permission:** `npm install` or any command that modifies project dependencies, pushing to origin. | ||
|
|
||
| ### Quality Assurance | ||
|
|
||
| After a batch of code changes, verify that QA scripts pass (`lint`, `typecheck`, `test`) and that the package builds correctly before considering the work done. | ||
|
|
||
| ### General Rules | ||
|
|
||
| - Read existing code before modifying it. Understand the patterns in use. | ||
| - Keep changes minimal and focused. Do not refactor surrounding code unless asked. | ||
| - Follow existing conventions. Do not introduce new patterns without discussion. | ||
|
|
||
| ### Code Style | ||
|
|
||
| - TypeScript strict mode. All compiler options in `tsconfig.json` are intentional. | ||
| - Functional style. Prefer `const`, pure functions, and immutable data. | ||
| - No classes except for the effect type implementations. | ||
| - ESM imports with `.js` extensions (e.g., `import foo from "./foo.js"`). | ||
| - Use Biome for formatting and linting. Do not override Biome rules without discussion. | ||
|
|
||
| ### Writing Style | ||
|
|
||
| - Be strict and concise. No filler, no fluff. | ||
| - Never use em dashes in docs, comments, or commit messages. Use commas, periods, or parentheses instead. | ||
| - Markdown files (docs, ADRs) should have lines wrapped at 120 characters max. | ||
|
|
||
| ### Testing | ||
|
|
||
| - Co-locate tests with source: `foo.ts` -> `foo.spec.ts` | ||
| - Use vitest (`describe`, `it`, `expect`) | ||
| - Test behavior, not implementation. Prefer testing public API surfaces. | ||
| - Algebraic law tests (functor, monad, applicative) use shared helpers from `src/testUtils/` | ||
|
|
||
| ### Commits & PRs | ||
|
|
||
| - Use conventional commits (e.g., `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`). | ||
| - One logical change per commit. | ||
| - PR descriptions should explain the "why", not just the "what". | ||
| - CI must pass: lint, typecheck, test, build. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # OK-FP | ||
|
|
||
| Essential Effect Data Types for TypeScript. | ||
|
|
||
| ## Project Structure | ||
|
|
||
| Single-package library (not a monorepo): | ||
|
|
||
| - `src/` - Source code, one directory per effect type | ||
| - `docs/` - VitePress documentation site | ||
| - `dist/` - Build output (not committed) | ||
|
|
||
| Each effect type follows the same layout: | ||
|
|
||
| - `src/{type}/` - Implementation directory | ||
| - `src/{type}.ts` - Barrel re-export | ||
| - `src/{type}/model.ts` - Type definitions | ||
| - `src/{type}/constructors.ts` - Factory functions | ||
| - `src/{type}/helpers.ts` - Utility functions | ||
| - `src/{type}/{type}.ts` - Core implementation | ||
| - `src/{type}/{type}.spec.ts` - Tests | ||
| - `src/{type}/constructors.spec.ts` - Constructor tests | ||
| - `src/{type}/helpers.spec.ts` - Helper tests | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - **Runtime:** Node.js (ESM) | ||
| - **Language:** TypeScript (strict mode) | ||
| - **Build:** tsdown | ||
| - **Test:** vitest (with `@vitest/coverage-v8`) | ||
| - **Lint:** Biome | ||
| - **Docs:** VitePress | ||
|
|
||
| ## Commands | ||
|
|
||
| ```bash | ||
| npm run lint # Run Biome check | ||
| npm run typecheck # Run tsc --noEmit | ||
| npm run test # Run vitest | ||
| npm run build # Build with tsdown | ||
| npm run docs:dev # Dev server for docs | ||
| npm run docs:build # Build docs | ||
| ``` | ||
|
|
||
| ## Key Conventions | ||
|
|
||
| - Tests are co-located with source files using `.spec.ts` suffix | ||
| - Imports use `.js` extensions (ESM convention for TypeScript) | ||
| - `clearMocks: true` is set globally in vitest config. No need to manually reset mocks. | ||
| - Each effect type has its own barrel export in `src/{type}.ts` | ||
| - Package exports are per-effect: `ok-fp/option`, `ok-fp/either`, `ok-fp/validation`, `ok-fp/task`, `ok-fp/taskEither` | ||
| - Test utilities for algebraic laws (functor, monad, applicative) live in `src/testUtils/` | ||
|
|
||
| ## Agent Guidelines | ||
|
|
||
| ### Permissions | ||
|
|
||
| - **Allowed without asking:** non-mutating npm scripts (`lint`, `typecheck`, `test`, `build`), reading project files, creating commits. | ||
| - **Requires permission:** `npm install` or any command that modifies project dependencies, pushing to origin. | ||
|
|
||
| ### Quality Assurance | ||
|
|
||
| After a batch of code changes, verify that QA scripts pass (`lint`, `typecheck`, `test`) and that the package builds correctly before considering the work done. | ||
|
|
||
| ### General Rules | ||
|
|
||
| - Read existing code before modifying it. Understand the patterns in use. | ||
| - Keep changes minimal and focused. Do not refactor surrounding code unless asked. | ||
| - Follow existing conventions. Do not introduce new patterns without discussion. | ||
|
|
||
| ### Code Style | ||
|
|
||
| - TypeScript strict mode. All compiler options in `tsconfig.json` are intentional. | ||
| - Functional style. Prefer `const`, pure functions, and immutable data. | ||
| - No classes except for the effect type implementations. | ||
| - ESM imports with `.js` extensions (e.g., `import foo from "./foo.js"`). | ||
| - Use Biome for formatting and linting. Do not override Biome rules without discussion. | ||
|
|
||
| ### Writing Style | ||
|
|
||
| - Be strict and concise. No filler, no fluff. | ||
| - Never use em dashes in docs, comments, or commit messages. Use commas, periods, or parentheses instead. | ||
| - Markdown files (docs, ADRs) should have lines wrapped at 120 characters max. | ||
|
|
||
| ### Testing | ||
|
|
||
| - Co-locate tests with source: `foo.ts` -> `foo.spec.ts` | ||
| - Use vitest (`describe`, `it`, `expect`) | ||
| - Test behavior, not implementation. Prefer testing public API surfaces. | ||
| - Algebraic law tests (functor, monad, applicative) use shared helpers from `src/testUtils/` | ||
|
|
||
| ### Commits & PRs | ||
|
|
||
| - Use conventional commits (e.g., `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`). | ||
| - One logical change per commit. | ||
| - PR descriptions should explain the "why", not just the "what". | ||
| - CI must pass: lint, typecheck, test, build. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| --- | ||
| status: "accepted" | ||
| date: 2025-12-28 | ||
| --- | ||
|
|
||
| # Use object-with-methods API style | ||
|
|
||
| ## Context and Problem Statement | ||
|
|
||
| Each effect data type (Option, Either, Task, etc.) exposes operations like `map`, `flatMap`, and `match`. | ||
| We need to decide how these operations are surfaced to the consumer: as standalone (free) functions | ||
| or as methods on the effect instance. | ||
|
|
||
| Most FP languages and libraries (e.g., Haskell, fp-ts) use free functions. This keeps each | ||
| function pure and composable, but in TypeScript it leads to deeply nested calls or requires | ||
| a `pipe` utility to read naturally: | ||
|
|
||
| ```ts | ||
| // Free functions + pipe (fp-ts style) | ||
| pipe( | ||
| some("Alice"), | ||
| map((name) => `Hello, ${name}!`), | ||
| getOrElse(() => "User not found"), | ||
| ); | ||
| ``` | ||
|
|
||
| ```ts | ||
| // Object with methods | ||
| some("Alice") | ||
| .map((name) => `Hello, ${name}!`) | ||
| .getOrElse(() => "User not found"); | ||
| ``` | ||
|
|
||
| ## Considered Options | ||
|
|
||
| - **Free functions.** Each operation is a standalone function (e.g., `map(option, fn)`). | ||
| Requires a `pipe` or `flow` helper for readable chaining. | ||
| - **Object with methods.** Each effect instance exposes operations as methods | ||
| (e.g., `option.map(fn)`). Chaining is native via dot notation. | ||
|
|
||
| ## Decision Outcome | ||
|
|
||
| Chosen option: **object with methods**, because: | ||
|
|
||
| 1. Method chaining reads naturally in TypeScript and requires no extra utilities (`pipe`, `flow`). | ||
| 2. IDE autocompletion shows available operations directly on the value. | ||
| 3. It matches what TypeScript developers already expect from APIs like `Array`, `Promise`, and `Map`. | ||
|
|
||
| For additional context, read the | ||
| ["Source Code Is Not Going Anywhere... I Hope"](https://pwlmc.dev/posts/ok-fp-breaks-a-core-functional-programming-rule-on-purpose/) | ||
| blog post where the reasons for the decision are discussed in more depth. | ||
|
|
||
| ### Consequences | ||
|
|
||
| - All effect data types are implemented as objects with methods. | ||
| - No `pipe` or `flow` utility is needed or provided. | ||
| - Free function helpers (e.g., `map2`, `all`) are still used where an operation applies | ||
| across multiple instances rather than being called on one. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| --- | ||
| status: "{proposed | rejected | accepted | deprecated | … | superseded by ADR-0123}" | ||
| date: { YYYY-MM-DD when the decision was last updated } | ||
| --- | ||
|
|
||
| # {short title, representative of solved problem and found solution} | ||
|
|
||
| ## Context and Problem Statement | ||
|
|
||
| {Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story. You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.} | ||
|
|
||
| ## Considered Options | ||
|
|
||
| - {title of option 1} | ||
| - {title of option 2} | ||
| - {title of option 3} | ||
| - … <!-- numbers of options can vary --> | ||
|
|
||
| ## Decision Outcome | ||
|
|
||
| Chosen option: "{title of option 1}", because {justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}. | ||
|
|
||
| <!-- This is an optional element. Feel free to remove. --> | ||
|
|
||
| ### Consequences | ||
|
|
||
| - Good, because {positive consequence, e.g., improvement of one or more desired qualities, …} | ||
| - Bad, because {negative consequence, e.g., compromising one or more desired qualities, …} | ||
| - … <!-- numbers of consequences can vary --> |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.