monadness

TypeScript/JavaScript library of Maybe, Either and Tuple Monads.


Keywords
either, maybe, tuple, functional, functional programming, functor, applicative, monad
License
MIT
Install
npm install monadness@0.5.2

Documentation

Build Status Coverage Status npm version

monadness-js

A TypeScript/JavaScript library that implements some basic "objects" of functional programming.

The advantages of using these classes:

  • Code that's easier to reason about.
  • Better error handling.
  • Avoid null and undefined issues.

Install

npm i -S monadness

Quick Examples

Example code for Maybe:

import { Maybe } from "monadness";

function getFileContents (filePath: string): Maybe<string> {
    /* Code to sync read file */
    return Maybe.fromNull(contents);
}

const contents = getFileContents("path/to/filename.json");

if (contents.isDefined()) {
    console.log("File contents:", contents.get());
} else {
    console.error("File error: File not loaded.");
}

console.log("File contents:", contents.getOrElseGet(""));

Example code for Either:

import { Either } from "monadness";

function getFileContents (filePath: string): Either<Error, string> {

    /* Code to sync read file */

    if (contents === undefined) {
        return Either.left<Error, string>(new Error(`File ${filePath} not found.`);
    } else {
        return Either.right<Error, string>(contents);
    }
}

const contents = getFileContents("path/to/filename.json");

if (contents.isRight()) {
    console.log("File contents:", contents.get());
} else {
    console.error("File error:", contents.getLeft());
}

contents.cata(
    content => console.log("File contents:", content),
    err => console.error("File error:", err),
);

Example code for Tuples:

import { Tuples } from "monadness";

const tuple = Tuples.from(1, "two", true);

const [a, b, c] = tuple;
console.log(a, b, c);
// 1, "two", true

const newTuple1 = tuple.map(a => a + a);
console.log(newTuple1._1, newTuple1._2, newTuple1._3);
// 2, "twotwo", 2

const newTuple2 = tuple.mbind(Tuples.from(
    a => Tuples.from(a + a),
    b => Tuples.from(b + "three"),
    c => Tuples.from(!! c),
));
console.log(newTuple2._1, newTuple2._2, newTuple2._3);
// 2, "twothree", false

Usage

TypeScript / ES6

import { Either, Maybe, Tuples } from "monadness";

NodeJS

const Monadness = require("monadness"),
      Either = Monadness.Either,
      Maybe = Monadness.Maybe,
      Tuples = Monadness.Tuples;

Browser

<script src="node_modules/dist/monadness.min.js"></script>
<script src="node_modules/dist/monadness.es6.min.js"></script>

Maybe

The Maybe class can be an instance with a defined value (Maybe.Just) or no value (Maybe.None).

This is a better alternative to throwing errors or returning undefined values. It lets the type-checker discover errors at compile time.

Static Methods

Maybe.just

Creates a Maybe.Just with the given value.

Maybe.just <T> (value: T): Maybe<T>

Maybe.none

Creates a Maybe.None.

Maybe.none <T> (): Maybe<T>

Maybe.nothing

Returns the Maybe.nothing singleton.

Maybe.nothing (): Maybe<void>

Maybe.fromNull

Creates a Maybe.Just with the given value or, if the value is null or undefined, a Maybe.None.

Maybe.fromNull <T> (value: T | undefined | null): Maybe<T>

Maybe.sequence

Turns an array of Maybes into a Maybe of arrays.

Maybe.sequence <T> (...maybes: Array<Maybe<T>>): Maybe<T[]>

Maybe.traverse

Maps an array into a Maybe of arrays.

Maybe.traverse <T, U> (f: (a: T) => Maybe<U>): (as: T[]) => Maybe<U[]>

Maybe.lift

Maybe.lift <T> (partialFunction: (...args: any[]) => T): (...args: any[]) => Maybe<T>

Instance Methods

isEmpty

Returns true if this is a Maybe.None.

isEmpty (): boolean

isDefined

Returns true if this is a Maybe.Right.

isDefined (): boolean

get

Returns the Just value or throws an error if None.

get (): T | never

getOrElse

Returns the Just value or the result of the given function.

getOrElse (value: () => T): T

getOrElseGet

Returns the Just value or the given value.

getOrElseGet (value: T): T

getOrThrow

Returns the Just value or throws the given error.

getOrThrow (err?: Error): T | never

orElse

Returns the Just value or the function result.

orElse (o: () => Maybe<T>): Maybe<T>

map

Applies the Maybe function to the value and return the result wrapped in a Maybe.

map <U> (f: (a: T) => U): Maybe<U>

fmap

Applies the function to the value and return the result.

fmap <U> (f: (a: T) => Maybe<U>): Maybe<U>

applies

applies <U, V> (f: (a: T) => (b: U) => Maybe<V>): (mb: Maybe<U>) => Maybe<V>

mbind

Applies the Maybe function to the value and return the result.

mbind <U> (f: Maybe<(a: T) => Maybe<U>>): Maybe<U>

flatten

Converts a Maybe of nested Maybes to a one-depth Maybe.

flatten <U> (): Maybe<U>

toEither

Converts the Maybe to an Either.

toEither (): Either.Right<Error, T>

toObject

Returns an object or null.

toObject (): { just: T | null; }

toJSON

Returns an object or null.

toJSON (): { just: T | null }

toString

Returns a string representation of the Maybe: '{ "just": T }'

toString (): string

equals

Tests the equality of two Maybes.

equals (other: Maybe<T>): boolean

Either

An Either can hold one of two values, a Left or Right value. Conventionly, a success is a right value and an error is a left value. (Right is right and left is wrong.)

This is a better alternative to throwing errors or returning undefined values. It lets the type-checker discover errors at compile time.

Static Methods

right

Returns an Either.Right with the given value.

Either.right <L, R> (right: R)

left

Returns an Either.Left with the given value.

Either.left <L, R> (left: L)

nothing

Returns the Either.nothing singleton.

Either.nothing (): Left<void, void>

sequence

Converts an array of Eithers into an Either of an array.

Either.sequence <L, R> (...eithers: Array<Either<L, R>>): Either<L, R[]>

traverse

Maps an array into an Either of an array.

Either.traverse <L, R, S> (f: (a: R) => Either<L, S>): (as: R[]) => Either<L, S[]>

lift

Either.lift <Error, T> (partialFunction: (...args: any[]) => T): (...args: any[]) => Either<Error, T>

Instance Methods

isLeft

Returns true if this is an Either.Left.

isLeft (): boolean

isRight

Returns true if this is an Either.Right.

isRight (): boolean

get

Returns the right value, or throws an error if Either.Left.

get (): R | never;

getRight

Returns the right value, or throws an error if Either.Left.

getRight (): R | never

getLeft

Returns the left value, or throws an error if Either.Right.

getLeft (): L | never

getOrElse

Returns the right value or the result of the given function.

getOrElse (f: () => R): R

getOrElseGet

Returns the right value or the given value.

getOrElseGet (right: R): R

getOrThrow

Returns the right value or throws the given error.

getOrThrow (err?: Error): R | never

orElse

Returns the right value or the result of the given function.

orElse (f: () => Either<L, R>)

map

Applies the function to the value and wraps the result in another Either.

map <S> (f: (a: R) => S): Either<L, S>

fmap

Applies the function to the value and returns the result.

fmap <S> (f: (a: R) => Either<L, S>): Either<L, S>

applies

applies <S, T> (f: ApplicativeFunc<R, S, T>): (mb: Either<L, S>) => Either<L, T>

###mbind

Basically maps the right value to the Either.Right function.

mbind <S> (f: Either<L, (f: R) => Either<L, S>>): Either<L, S>

bimap

Applies the applicable function to the value and produces an Either.

bimap <M, S> (lf: (l: L) => M, rf: (r: R) => S): Either<M, S>

cata

Applies the applicable function to the Either and produces a value.

cata <V> (lf: (l: L) => V, rf: (r: R) => V): V

flatten

Flattens an Either of nested Eithers to a one depth Either.

flatten <M, S> (): Either <M, S>

toMaybe

Converts this Either to a Maybe.

toMaybe (): Maybe<R>

toObject

Converts the instance to a basic object.

toObject (): { left?: L; right?: R }

toJSON

Allows the instance to be converted to JSON.

toJSON (): { left?: L; right?: R }

toString

Returns the Either as a string: '{"right": R}' or '{"left": L}'

toString (): string

equals

Tests the equality of the Eithers.

equals (other: Either<L, R>): boolean

Tuples

Monadness tuples are simply arrays with some extra methods.

Static Methods

Tuples.from

Tuples.from <T1, ...Tn> (_1: T1, ..._n: Tn): TupleN <T1, ...Tn> & [T1, ...Tn]

Instance Getters

Accesses the values of the tuple (not zero-based).

_1: T1;
_n: Tn;

Instance Methods

In addition to all of the regular array methods, the Tuple class has the following methods.

map

Applies the function to each value of the tuple.

map <U1, ...Un> (f: (a: T1 | ...Tn) => U1 | ...Un): Tuple9<U1, ...Un>

fmap

Applies the function to each value of the tuple.

fmap <U1, ...U9> (f: (a: T1 | ...Tn) => Tuple1<U1 | ...Un>): Tuple9<U1, ...Un>

applies

applies <U1, U2, ...Un, V1, ...Vn> (f: ApplicativeFunc<T1 | ...Tn, U1 | ...Un, V1 | ...Vn>): (mb: Tuple9<U1, ...Un>) => Tuple9<V1, ...Vn>

mbind

Applies each function to each value respectively.

mbind <U1, ...Un> (f: TupleN<TMF<T1, U1>, ...TMF<Tn, Un>>): TupleN<U1, ...Un>

toJSON

Converts the tuple to a JSON object.

toJSON (): { just: T | null }

equals

Tests the equality of two tuples.

equals (other: TupleN<T>): boolean

Example of Static Initializers and Usage:

import { Tuples } from "monadness";

const tuple0 = Tuples.from();
const tuple1 = Tuples.from("a");
const tuple2 = Tuples.from("a", 2);
const tuple3 = Tuples.from(123, "abc", true);
const tuple4 = Tuples.from(1, 2, 3, 4);
const tuple5 = Tuples.from(1, 2, 3, 4, 5);
const tuple6 = Tuples.from(1, 2, 3, 4, 5, 6);
const tuple7 = Tuples.from(1, 2, 3, 4, 5, 6, 7);
const tuple8 = Tuples.from(1, 2, 3, 4, 5, 6, 7, 8);
const tuple9 = Tuples.from(1, 2, 3, 4, 5, 6, 7, 8, 9);

console.log("First value:", tuple2._1, tuple[0]);
console.log("Second value:", tuple2._2, tuple[1]);

const [x, y] = tuple2; // Desctructuring

for (const i in tuple4) {
    console.log("Tuple4 values:", tuple4[i]);
}

for (const val of tuple5) {
    console.log("Tuple5 values:", val);
}

License

(The MIT License)

Copyright (c) 2017 Patrick Martin

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.