monas
v0.3.3
Published
Scala like monads for javascript
Downloads
11
Readme
Monas (from Greek μονάς - "singularity") is a monad library for JavaScript apps. It's been inspired by Scala and developed with TypeScript. Monas introduces two fundamental monads: Option<A>
and Either<A, B>
.
$ yarn add monas
Option<A>
Represents optional values. Instances of Option are either an instance of Some
or the object None
.
The idea is to get rid of null
and undefined
and, thus, eliminate null pointer exceptions, reduce branching (if statement) and produce better code.
| Exported | Description |
| ------ | ----------- |
| Option<A>
| the base abstract class that implements utility functions for instances of classes Some
and None
. It's primarily used to indicate that a type has an optional value. |
| Some<A>
| one of the possible implementations of Option<A>
that wraps a value. The incapsulated value is available by the get()
method. |
| None<A>
| another implementation of Option<A>
indicating absence of value.|
| some<A>(x: A): Option<A>
| a helper function instantiating objects of type Some
or None
based on a provided value.|
| none: Option<A>
| a single instance of None<A>
. |
let greeting: Option<string> = some('Hello world');
greeting = some(null); // will be none
assert(greeting === none);
some()
will return none
if a given argument is null
or undefined
:
Reading data from Option
:
a) getOrElse()
:
let str: string = greeting.getOrElse('');
// Returns greeting or empty string if none.
b) get()
:
let str: string = greeting.get();
// Returns greeting or throws an exception.
c) Using Symbol.Iterable
:
let [str]: string = [...greeting];
// returns ["something"] or empty array [] if none.
OR
for(let str: string of greeting) {
assert(str, "something");
}
Examples
The most idiomatic way to use an Option
instance is to treat it as a collection or monad and use map, flatMap, filter, or foreach.
Let's consider an example where for a given country code we need to find the country name or print "N/A" if it's not found.
import { Option, none, some } from './Option';
type Country = { name: string, code: number };
let countries: Country[] = [{ name: 'United States', code: 1 }, { name: 'United Kingdom', code: 44 }];
function getNameByCode(code: number): string {
// find a country by code
const country = countries.find(c => c.code === code);
// if the country is not null return the name or N/A
return some(country).map(_ => _.name).getOrElse('N/A');
// ^^^ ^^^ select name ^^^ get a value if exists
// create Option<Country> otherwise use 'N/A'
}
More examples could be found here.
Either<A, B>
Represents a value of one of two possible types (a disjoint union).
An instance of Either is an instance of either Left
or Right
.
Convention dictates that Left is used for failure and Right is used for success.
| Exported | Description |
| ------ | ----------- |
| Either<A, B>
| the base abstract class that implements utility functions for instances of classes Left
and Right
. |
| Right<A, B>
| a right "good" part. |
| Left<A, B>
| a left "fail over" part, e.g. Error. |
| right<A, B>(x: B): Either<A, B>
| a helper function instantiating Right
objects. |
| left<A, B>(x: B): Either<A, B>
| a helper function instantiating Left
objects. |
Generally Either
can be considered as an alternative to Option
where instead of
None
a useful information could be encapsulated into Left
.
It turns out that Either
is a power-full type for validations
since it can return either a successfully parsed value, or a validation error.
Either<A, B>
can be instantiated by calling right(something)
or left(error)
:
let eitherNumber: Either<Error, number> = right(42); // equivalent to new Right(42)
OR
let eitherNumber: Either<Error, number> = left(Error('Not a number')); // equivalent to new Left('Not a number')
Either
is a right-biased monad:
let eitherNum: Either<number, Error> = right<number, Error>(42);
eitherNum.map(num => num * 2).getOrElse(-1);
// returns 84
Another example:
let eitherNum: Either<number, Error> = left<number, Error>(Error('Not a number.'));
eitherNum.map(_ => _ * 2).getOrElse(-1);
// returns -1
Use mapLeft()
, foreachLeft()
to handle Left
values:
function print(numberOrError: Either<Error, number>) {
numberOrError
.map(num => num)
.foreach(printNumber)
.mapLeft(err => err.message)
.foreachLeft(printError);
}
Use swap()
function to swap Left
and Right
.
left<number, string>('Not a number').swap().getOrElse('');
// returns 'Not a number'
Either
implements Symbol.Iterable
:
let eitherNumber = right<number, Error>(42).map(_ => _ * 2);
let [n] = [...eitherNumber]; // n === 42
for(let num of eitherNumber) {
assert(num, 42);
}
More examples could be found here.
Docs
License
MIT