https://github.com/dominicegginton/functional-reader
A purely functional, dependency-free dependency injection library for TypeScript based on the Reader Monad.
https://github.com/dominicegginton/functional-reader
dependency-injection functional-programming typescript
Last synced: 19 days ago
JSON representation
A purely functional, dependency-free dependency injection library for TypeScript based on the Reader Monad.
- Host: GitHub
- URL: https://github.com/dominicegginton/functional-reader
- Owner: dominicegginton
- Created: 2026-05-15T20:17:38.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-15T21:51:22.000Z (about 1 month ago)
- Last Synced: 2026-05-16T00:10:54.480Z (about 1 month ago)
- Topics: dependency-injection, functional-programming, typescript
- Language: TypeScript
- Homepage:
- Size: 62.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# functional-reader
A purely functional, dependency-free dependency injection library for TypeScript based on the Reader Monad.
## Features
- **Purely Functional**: Built on the Reader Monad pattern.
- **Type-Safe**: Full TypeScript support with excellent type inference.
- **Zero Dependencies**: Lightweight and fast.
- **Composable**: Utilities for mapping, chaining, and adapting environments.
- **Curried**: All functions are fully curried for maximum flexibility.
## Comparison with Other Libraries
While many libraries offer functional programming and dependency management in TypeScript, **functional-reader** focuses on being a lightweight, specialized tool.
| Feature / Goal | functional-reader | fp-ts (Reader) | lodash/fp |
|------------------------------------------------|-------------------------|--------------------------|---------------------------------|
| Reader Monad / Dependency Injection | Core feature | Included module | Not available |
| Zero runtime dependencies | Yes | No (requires fp-ts) | No (lodash dependency) |
| TypeScript support & inference | Excellent | Excellent | Partial (not Reader-aware) |
| Lightweight / Minimal bundle | Yes | No (fp-ts is large) | No (lodash is large) |
| API Simplicity | Minimal, approachable | Broader/complex | Simple, but not monadic |
| Curried / Point-Free APIs | Yes | Yes | Partial |
| Explicit error handling (neverthrow compatible)| Yes | Yes (with effort) | No |
| Do-notation & record chaining helpers | Included | Included | No |
| Pure FP without typeclass bloat | Yes | No (full typeclass) | Yes (pure, but not Reader) |
### Why functional-reader?
- **Focused**: A small, dependency-free implementation of the Reader monad.
- **TypeScript-native**: Designed for first-class type inference and autocompletion.
- **Lightweight**: Zero dependencies and minimal bundle size overhead.
- **Approachable**: A readable API that is easy to learn and integrate.
- **Interoperable**: Designed to play well with other modern libraries like `neverthrow`.
## Compatibility & Interoperability
**functional-reader** is unopinionated, making it easy to integrate into existing TypeScript projects:
| Interop Scenario | Support Description |
|---------------------------------------------|------------------------------------------------------------------------------|
| **neverthrow** | Returns or operates on `Result` out-of-the-box. |
| **fp-ts** | Compatible API surface; easily migrated or combined. |
| **RxJS / Promises** | Readers can be evaluated inside observables or async flows. |
| **Legacy Code** | Since a Reader is just `(env) => value`, it plugs in directly everywhere. |
| **Standard Utilities** | Works seamlessly with standard JS utilities (lodash, ramda, etc.). |
## Install
> **Note**: functional-reader is distributed directly via this repository to emphasize transparency, autonomy, and source engagement over reliance on third-party registries. This approach allows users to review, audit, and tailor the library to their needs, while avoiding potential risks or restrictions imposed by centralized package platforms.
```bash
# Install the latest version from GitHub
npm install github:dominicegginton/functional-reader
# Or install a specific version/tag
npm install github:dominicegginton/functional-reader#v1.0.0
```
## Documentation
### Core Types
- `Reader`: A computation that requires an environment `Env` to produce a result `Result`.
### Construction
- `of(a: A)`: Creates a Reader that always returns `a`.
- `defer(f: () => Reader)`: Creates a Reader that is lazily initialized when it is called.
- `ask()`: Creates a Reader that returns the entire environment.
- `asks(selector: (env: Env) => A)`: Creates a Reader that selects a part of the environment.
- `asksReader(f: (env: Env) => Reader)`: Creates a Reader that selects a part of the environment and returns a new Reader.
- `prop(key: K)`: Creates a Reader that extracts a specific property from the environment.
- `pick(keys: readonly K[])`: Creates a Reader that selects multiple properties from the environment.
### Transformation & Composition
- `map(f: (a: A) => B)`: Transforms the result of a Reader.
- `mapTo(value: B)`: Maps the result of a Reader to a constant value.
- `chain(f: (a: A) => Reader)`: Sequences two Readers where the second depends on the first.
- `chainFirst(f: (a: A) => Reader)`: Sequences two Readers where the second depends on the first, but returns the result of the first.
- `flatten(mma: Reader>)`: Collapses a nested Reader into a single Reader.
- `local(f: (outer: EnvOuter) => EnvInner)`: Adapts a Reader to a different environment type.
- `provide(env: Env)`: Provides an environment to a Reader, resulting in a Reader that requires no environment.
- `ap(fa: Reader)`: Applies a function contained within a Reader to a value in another Reader.
### Sequencing & Side Effects
- `chainRight(rb: Reader)`: Sequences two Readers and returns the result of the second.
- `chainLeft(rb: Reader)`: Sequences two Readers and returns the result of the first.
- `tap(sideEffect: (a: A, env: Env) => void)`: Performs a side effect without changing the Reader's result.
### Combinators
- `sequence(readers: Array>)`: Combines an array of Readers into one that returns an array of results.
- `traverse(f: (a: A) => Reader)`: Maps over an array of values using a function that returns a Reader.
- `struct(readers: S)`: Combines an object of Readers into one that returns an object of results.
- `zip(rb: Reader)`: Combines two Readers into a single Reader that returns a tuple of their results.
- `iif(predicate: (env: Env) => boolean, onTrue: Reader, onFalse: Reader)`: Conditionally chooses between two Readers based on the environment.
- `when(predicate: (env: Env) => boolean, reader: Reader)`: Conditionally executes a Reader if the predicate is true.
- `alt(secondary: Reader)`: Returns the result of the first Reader if it is not null or undefined, otherwise returns the result of the second Reader.
### Do-Notation
- `Do()`: Initializes a "Do" block for chainable record compositions.
- `bindTo(key: K)`: Wraps a Reader's result into a record with a specified key.
- `bind(key: K, f: (a: A) => Reader)`: Binds the result of a Reader to a key in a "Do" block.
### Functional Utilities
- `pipe(a, ...fns)`: Pipes a value through a series of functions.
- `flow(...fns)`: Composes multiple functions into a single function.