eaux
Version:
A lightweight and functional-style library that provides robust abstractions for handling optional values and handling operations that can either succeed or fail. By making states explicit, it encourages precise and deliberate management of application lo
334 lines (204 loc) • 10.9 kB
Markdown
# `eaux`
A lightweight and functional-style library that provides robust abstractions for handling optional values and handling operations that can either succeed or fail. By making states explicit, it encourages precise and deliberate management of application logic.
---
## Overview
**`eaux`** introduces two core abstractions:
- **`Maybe`**
Represents an optional value. A `Maybe` can either be a `Nothing` containing no value or a `Something` containing a value.
- **`Result`**
Represents the outcome of an operation that can either succeed or fail. A `Result` can either be a `Failure` containing an error or a `Success` containing a value.
Both abstractions support a suite of chainable and composable operations that lead to code that is clear, consistent, and reliable.
---
## Installation
To install **`eaux`**, ensure that you have Node.js version 20 or newer, and use your preferred package manager.
### `npm`
```bash
npm install eaux
```
### `pnpm`
```bash
pnpm add eaux
```
### `yarn`
```bash
yarn add eaux
```
---
## API
Below is a complete overview of the functions and methods exposed by the library.
### `Maybe`
#### Functions
- **`something<TValue = any>(value: TValue): Maybe<TValue>`**
Creates and returns a new `Something` `Maybe` containing the provided `value`.
- **`nothing<TValue = any>(): Maybe<TValue>`**
Creates and returns a new `Nothing` `Maybe`.
- **`isMaybe(value: unknown): value is Maybe<unknown>`**
Checks if the provided `value` is a `Maybe`.
#### Methods
On a `Maybe` instance, the following methods are available:
- **`and<TOtherValue>(other: Maybe<TOtherValue>): Maybe<TOtherValue>`**
If this is a `Something` `Maybe`, returns `other`.
If this is a `Nothing` `Maybe`, returns this `Maybe`.
- **`andThen<TOtherValue>(f: (value: TValue) => Maybe<TOtherValue>): Maybe<TOtherValue>`**
If this is a `Something` `Maybe`, returns the result of applying `f` to the contained value.
If this is a `Nothing` `Maybe`, returns this `Maybe`.
- **`expect(message: string): TValue`**
If this is a `Something` `Maybe`, returns the contained value.
If this is a `Nothing` `Maybe`, throws an [`ExpectationError`][src-error-expectation-ts-file] with the provided `message`.
- **`filter(predicate: (value: TValue) => boolean): Maybe<TValue>`**
If this is a `Something` `Maybe`, returns this `Maybe` if the contained value satisfies the provided `predicate`.
If this is a `Nothing` `Maybe`, returns this `Maybe`.
- **`getSuccessOr<TError>(error: TError): Result<TValue, TError>`**
If this is a `Something` `Maybe`, returns a `Success` `Result` containing the contained value.
If this is a `Nothing` `Maybe`, returns a `Failure` `Result` containing the provided `error`.
- **`inspect(f: (value: TValue) => void): Maybe<TValue>`**
If this is a `Something` `Maybe`, applies `f` to the contained value and returns this `Maybe`.
If this is a `Nothing` `Maybe`, returns this `Maybe`.
- **`isNothing(): boolean`**
If this is a `Something` `Maybe`, returns `false`.
If this is a `Nothing` `Maybe`, returns `true`.
- **`isNothingOr(predicate: (value: TValue) => boolean): boolean`**
If this is a `Something` `Maybe`, returns the result of applying `predicate` to the contained value.
If this is a `Nothing` `Maybe`, returns `true`.
- **`isSomething(): boolean`**
If this is a `Something` `Maybe`, returns `true`.
If this is a `Nothing` `Maybe`, returns `false`.
- **`isSomethingAnd(predicate: (value: TValue) => boolean): boolean`**
If this is a `Something` `Maybe`, returns the result of applying `predicate` to the contained value.
If this is a `Nothing` `Maybe`, returns `false`.
- **`map<TNewValue>(f: (value: TValue) => TNewValue): Maybe<TNewValue>`**
If this is a `Something` `Maybe`, returns a `Something` `Maybe` containing the result of applying `f` to the contained value.
If this is a `Nothing` `Maybe`, returns this `Maybe`.
- **`or(other: Maybe<TValue>): Maybe<TValue>`**
If this is a `Something` `Maybe`, returns this `Maybe`.
If this is a `Nothing` `Maybe`, returns `other`.
- **`unwrap(): TValue`**
If this is a `Something` `Maybe`, returns the contained value.
If this is a `Nothing` `Maybe`, throws an [`ImproperUnwrapError`][src-error-improper-unwrap-ts-file].
---
### `Result`
#### Functions
- **`success<TValue = any, TError = any>(value: TValue): Result<TValue, TError>`**
Creates and returns a new `Success` `Result` containing the provided `value`.
- **`failure<TValue = any, TError = any>(error: TError): Result<TValue, TError>`**
Creates and returns a new `Failure` `Result` containing the provided `error`.
- **`isResult(value: unknown): value is Result<unknown, unknown>`**
Checks if the provided `value` is a `Result`.
#### Methods
On a `Result` instance, the following methods are available:
- **`and<TOtherValue>(other: Result<TOtherValue, TError>): Result<TOtherValue, TError>`**
If this is a `Success` `Result`, returns `other`.
If this is a `Failure` `Result`, returns this `Result`.
- **`andThen<TOtherValue>(f: (value: TValue) => Result<TOtherValue, TError>): Result<TOtherValue, TError>`**
If this is a `Success` `Result`, returns the result of applying `f` to the contained value.
If this is a `Failure` `Result`, returns this `Result`.
- **`expect(message: string): TValue`**
If this is a `Success` `Result`, returns the contained value.
If this is a `Failure` `Result`, throws an [`ExpectationError`][src-error-expectation-ts-file] with the provided `message`.
- **`expectFailure(message: string): TError`**
If this is a `Success` `Result`, throws an [`ExpectationError`][src-error-expectation-ts-file] with the provided `message`.
If this is a `Failure` `Result`, returns the contained error.
- **`getFailure(): Maybe<TError>`**
If this is a `Success` `Result`, returns a `Nothing` `Maybe`.
If this is a `Failure` `Result`, returns a `Something` `Maybe` containing the contained error.
- **`getSuccess(): Maybe<TValue>`**
If this is a `Success` `Result`, returns a `Something` `Maybe` containing the contained value.
If this is a `Failure` `Result`, returns a `Nothing` `Maybe`.
- **`inspect(f: (value: TValue) => void): Result<TValue, TError>`**
If this is a `Success` `Result`, applies `f` to the contained value and returns this `Result`.
If this is a `Failure` `Result`, returns this `Result`.
- **`inspectFailure(f: (error: TError) => void): Result<TValue, TError>`**
If this is a `Success` `Result`, returns this `Result`.
If this is a `Failure` `Result`, applies `f` to the contained error and returns this `Result`.
- **`isFailure(): boolean`**
If this is a `Success` `Result`, returns `false`.
If this is a `Failure` `Result`, returns `true`.
- **`isFailureAnd(predicate: (error: TError) => boolean): boolean`**
If this is a `Success` `Result`, returns `false`.
If this is a `Failure` `Result`, returns the result of applying `predicate` to the contained error.
- **`isSuccess(): boolean`**
If this is a `Success` `Result`, returns `true`.
If this is a `Failure` `Result`, returns `false`.
- **`isSuccessAnd(predicate: (value: TValue) => boolean): boolean`**
If this is a `Success` `Result`, returns the result of applying `predicate` to the contained value.
If this is a `Failure` `Result`, returns `false`.
- **`map<TNewValue>(f: (value: TValue) => TNewValue): Result<TNewValue, TError>`**
If this is a `Success` `Result`, returns a `Success` `Result` containing the result of applying `f` to the contained value.
If this is a `Failure` `Result`, returns this `Result`.
- **`mapFailure<TNewError>(f: (error: TError) => TNewError): Result<TValue, TNewError>`**
If this is a `Success` `Result`, returns this `Result`.
If this is a `Failure` `Result`, returns a `Failure` `Result` containing the result of applying `f` to the contained error.
- **`or<TOtherError>(other: Result<TValue, TOtherError>): Result<TValue, TOtherError>`**
If this is a `Success` `Result`, returns this `Result`.
If this is a `Failure` `Result`, returns `other`.
- **`unwrap(): TValue`**
If this is a `Success` `Result`, returns the contained value.
If this is a `Failure` `Result`, throws an [`ImproperUnwrapError`][src-error-improper-unwrap-ts-file].
- **`unwrapFailure(): TError`**
If this is a `Success` `Result`, throws an [`ImproperUnwrapError`][src-error-improper-unwrap-ts-file].
If this is a `Failure` `Result`, returns the contained error.
---
## Usage
The following examples illustrate some real-world scenarios where **`eaux`** can be useful.
### Example 1: Handling Optional User Input
Assume you want to process user input that might be empty. With `Maybe`, you can ensure that every code path explicitly checks whether the value exists.
```typescript
import { nothing, something } from 'eaux'
// A function that simulates reading user input.
function getUserInput() {
// Imagine a real implementation here.
const randomNumber = Math.random()
return (randomNumber > 0.5) ?
'Hello, world!' :
null
}
const input = getUserInput()
const maybeInput = (input !== null) ?
something(input) :
nothing()
if (maybeInput.isSomething()) {
console.log('User input provided:', maybeInput.unwrap())
} else {
console.log('No user input was provided')
}
```
### Example 2: Parsing Numbers with `Result`
Imagine a routine that attempts to parse a string into a number. By using `Result`, you enforce error handling explicitly.
```typescript
import { failure, success } from 'eaux'
function parseNumber(input: string): Result<number, string> {
const parsedValue = Number(input)
// Check for non-numeric values.
if (isNaN(parsedValue)) {
return failure('Invalid number format, expected a numeric string.')
}
return success(parsedValue)
}
const result = parseNumber('42') // Try changing `'42'` to an invalid input.
if (result.isSuccess()) {
console.log('Number parsed:', result.unwrap())
} else {
console.error('Error parsing number:', result.unwrapFailure())
}
```
### Example 3: Transforming Values with Chainable Operations
Combine operations using chainable methods to build complex logic that is both clear and explicit.
```typescript
import { nothing, something } from 'eaux'
function doubleValue(value: number): number {
return (value * 2)
}
const maybeValue = something(21)
// Chain the operations: if the value exists, double it; otherwise, do nothing.
const transformedValue = maybeValue
.map(doubleValue)
.or(something(0))
console.log('Value transformed:', transformedValue.unwrap())
```
---
## License
This project is licensed under the Apache License, Version 2.0. See the accompanying [`LICENSE`][license-file] file for details.
---
[license-file]: ./LICENSE
[src-error-expectation-ts-file]: ./src/error/expectation.ts
[src-error-improper-unwrap-ts-file]: ./src/error/improper-unwrap.ts