npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@leanmind/monads

v1.1.0

Published

A collection of monads implemented in TypeScript using object-oriented programming.

Downloads

713

Readme

monads

This is a set of implementations of monads in TypeScript with OOP perspective.

Installation

You can install the package using npm:

npm install @leanmind/monads

Either Monad

The Either monad represents a value of one of two possible types (a disjoint union). An Either is either a Left or a Right. By convention, Right is used to hold a successful value, while Left is used to hold an error or failure.

Usage

Creating an Either

You can create an Either using the static methods Either.right and Either.left.

import { Either } from '@leanmind/monads';

// Creating a Right
const right = Either.right(42);

// Creating a Left
const left = Either.left('Error');

Creating Either from possible failed operations

You can create an Either from a failed operations using the static method Either.catch.

import { Either } from '@leanmind/monads';

const findUser = (id: number): User => {
  if (id === 42) {
    return { id: 42, name: 'John Doe' };
  }
  throw new Error('User with id ${id} not found');
};

const right = Either.catch<User>(() => findUser(42)); // Right({ id: 42, name: 'John Doe' })
const left = Either.catch<User>(() => findUser(1)); // Left(Error('User with id 1 not found'))

Mapping over an Either

You can use the flatMap or mapmethod to transform the value inside a Right, and flatMapLeft or mapLeft to transform the value inside a Left.

Using flatMap and flatMapLeft
import { Either } from '@leanmind/monads';m

const right = Either.right(42).flatMap(x => Either.right(x + 1)); // Right(43)
const left = Either.left('Error').flatMapLeft(err => Either.left(`New ${err}`)); // Left('New Error')
Using map and mapLeft
import { Either } from '@leanmind/monads';

const right = Either.right(42).map(x => x + 1); // Right(43)
const left = Either.left('Error').mapLeft(err => `New ${err}`); // Left('New Error')

Matching an Either

You can use the match method to handle both Right and Left cases and unwrap the result.

import { Either } from '@leanmind/monads';

const sucess = Either.right(42).match(
  err => `Error: ${err}`,
  x => (x + 1).toString()
); // '43'

const error = Either.left('Error').match(
  err => `Error: ${err}`,
  x => (x + 1).toString(),
); // 'Error: Error'

checking if an Either is Right or Left

You can check explicitly if an Either is Right or Left using the isRight and isLeft methods. Probably you will not need to use these methods, but they are available in case of refactoring from try-catch blocks or other situations.

import { Either } from '@leanmind/monads';

const right = Either.right(42);
const left = Either.left('Error');

right.isRight(); // true
right.isLeft(); // false
left.isRight(); // false
left.isLeft(); // true

Chaining operations

You can chain operations using the map, mapLeft, flatMap and flatMapLeft method.

The following example demonstrates how to chain operations using the map method:

import { Either } from '@leanmind/monads';

const result = Either.right(42)
  .map(x => x + 1)
  .map(x => x * 2)
  .match<string|number>(
    err => `Error: ${err}`,
    x => x
  );

console.log(result); // 86

Handling errors

Here is a complete example demonstrating the usage of the Either monad:

import { Either } from '@leanmind/monads';

function divide(a: number, b: number): Either<string, number> {
  if (b === 0) {
    return Either.left('Division by zero');
  }
  return Either.right(a / b);
}

const result = divide(10, 2)
  .map(x => x * 2)
  .match(
    err => `Error: ${err}`,
    value => `Result: ${value}`
  );

console.log(result); // 'Result: 10'

In this example, the divide function returns an Either that represents the result of the division or an error if the division is by zero. The result is then transformed and matched to produce a final string.

Option Monad

The Option monad represents a value that may or may not be present. An Option is either a Some or a None. Some is used to hold a value, while None is used to represent the absence of a value.

Usage

Creating an Option

You can create an Option using the static methods Option.of.

import { Option } from '@leanmind/monads';

// Creating a Some
const some = Option.of(42); // Some(42)

// Creating a None
const none = Option.of(null); // None

Retrieving the value of an Option

You can use the getOrElse method to retrieve the value of an Option or provide a default value if it is None.

import { Option } from '@leanmind/monads';

const some = Option.of(42);
some.getOrElse(0); // 42

const none = Option.of(null);
none.getOrElse(0); // 0

Filtering an Option

You can use the filter method to keep the Some value if it satisfies a predicate.

import { Option } from '@leanmind/monads';m

const some = Option.of(42).filter(x => x > 40); // Some(42)
const none = Option.of(42).filter(x => x > 50); // None

Mapping over an Option

You can use the flatMap or map method to transform the Some value.

Using flatMap
import { Option } from '@leanmind/monads';m

const some = Option.of(42).flatMap(x => Option.of(x + 1)); // Some(43)
const none = Option.of(null).flatMap(x => Option.of(x + 1)); // None
Using map
import { Option } from '@leanmind/monads';

const some = Option.of(42).map(x => x + 1); // Some(43)
const none = Option.of(null).map(x => x + 1); // None

Matching an Option

You can use the match method to handle both Some and None cases and unwrap the result.

import { Option } from '@leanmind/monads';

const some = Option.of(42).match(
  x => x + 1,
  () => 'No value'
); // 43

const none = Option.of(null).match(
  x => x + 1,
  () => 'No value'
); // 'No value'

Checking if an Option is Some or None

If needed, you can check explicitly if an Option is Some or None using the isSome and isNone methods.

import { Option } from '@leanmind/monads';

const some = Option.of(42);
some.isSome(); // true
some.isNone(); // false

const none = Option.of(undefined);
none.isSome(); // false
none.isNone(); // true

Try Monad

The Try monad represents a computation that may fail.

Usage

You can create a Try using the static method Try.execute.

import { Try } from '@leanmind/monads';

const success = Try.execute(() => 42); // Success(42)

const failure = Try.execute(() => {
  throw new Error('Error');
}); // Failure(Error('Error'))

Using map

You can use the map method to transform the value inside a Success.

import { Try } from '@leanmind/monads';m

const success = Try.execute(() => 42).map(x => x + 1); // Success(43)

Using flatMap

You can use the flatMap method to transform the value inside a Success with a fallible closure.

import { Try } from '@leanmind/monads';

const success = Try.execute(() => 42).flatMap(x => Try.execute(() => x + 1)); // Success(43)

Matching a Try

You can use the match method to handle both Success and Failure cases and unwrap the result.

import { Try } from '@leanmind/monads';

const success = Try.execute(() => 42).match(
  err => `Error: ${err}`,
  x => x + 1
); // 43

const failure = Try.execute(() => {
  throw new Error('Error');
}).match(
  err => `Error: ${err}`,
  x => x + 1
); // 'Error: Error'

Handling errors in Infrastructure code

Normally, Try is used to handle Exceptions that are raise by third party libraries

import { Try } from '@leanmind/monads';

const result = Try.execute(() => {
  // Some API of a library that may throw an exception
  return 42;
}).match(
  err => `Error: ${err}`,
  x => x + 1
);

console.log(result); // 43

Checking if a Try is Success or Failure

If needed, you can check explicitly if a Try is Success or Failure using the isSuccess and isFailure methods.

import { Try } from '@leanmind/monads';

const success = Try.execute(() => 42);
success.isSuccess(); // true

const failure = Try.execute(() => {
  throw new Error('Error');
});
failure.isFailure(); // true

Future Monad

The Future monad represents a computation that may be executed asynchronously.

Usage

Creating a Future

You can create a Future using the static method Future.of.

import { Future } from '@leanmind/monads';

const future = Future.of(() => Promise.resolve(42));

Mapping over a Future

You can use the map or flatMap method to transform the computed value inside a Future. The operation will not execute the transformation (lazy evaluation) until complete method is called.

Using flatMap
import { Future } from '@leanmind/monads';

const future = Future.of(() => Promise.resolve(42))
  .flatMap(x => Future.of(() => Promise.resolve(x + 1)))
  .complete(
    x => console.log(x),
    err => console.error(err)
  ); // 43
Using map
import { Future } from '@leanmind/monads';

const future = Future.of(() => Promise.resolve(42))
  .map(x => x + 1)
  .complete(
    x => console.log(x),
    err => console.error(err)
  ); // 43

Evaluate a Future

You can evaluate a Future using the complete method. The complete method takes two functions as arguments: one for the success case and one for the failure case.

import { Future } from '@leanmind/monads';

const successFuture = Future.of(() => Promise.resolve(42));

await successFuture.complete(
  x => console.log(x),
  err => console.error(err)
); // 42

const failureFuture = Future.of(() => Promise.reject(new Error('Error')));

await failureFuture.complete(
  x => console.log(x),
  err => console.error(err)
); // Error('Error')

IO Monad

The IO monad represents a computation that may have side effects.

In this way, the IO monad is used to encapsulate side effects in a pure functional way.

So, you can operate as pure functions until you call the runUnsafe method.

Usage

Creating an IO

You can create an IO using the static method IO.of.

import { IO } from '@leanmind/monads';

const io = IO.of(() => 42);

Mapping over an IO

You can use the flatMap or map method to concatenate IO operations.

The operation is not executed until you call the runUnsafe method.

Using flatMap
import { IO } from '@leanmind/monads';

const io = IO.of(() => 42).flatMap(x => IO.of(() => x + 1));

io.run(); // 43
Using map
import { IO } from '@leanmind/monads';

const io = IO.of(() => 42).map(x => x + 1);

io.runUnsafe(); // 43