An open API service indexing awesome lists of open source software.

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.

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.