jazzi
v4.2.0
Published
Implementations of common functional structures. Available structures and features:
Downloads
19
Readme
Jazzi: Juan's Algebraic Data Structures
Implementations of common functional structures. Available structures and features:
- Either
- Maybe
- Async
- All structures have a toPromise and toThenable methods
Installing
From npm registry
npm install jazzi
// or
yarn add jazzi
From denoland
import * as jazzi from 'https://deno.land/x/jazzi/mod.ts'
Usage
This README.md is a summary of all that is available in jazzi. For more details go to API.md
All structures and global functions are exported as named exports from the jazzi module
import { Maybe, Either, Async } from 'jazzi';
// or if you use deno
import { Maybe, Either, Async } from 'https://deno.land/x/jazzi/mod.ts';
Constructors and operators are exported as named exports of the structure they are meant to be used on.
import * as M from 'jazzi/Maybe';
M.of(40)['|>'](M.map(x => x + 2)) // Just 42
A wrapper that mimics a fluent interface is also available
import * as M from 'jazzi/Maybe/fluent';
M.of(40).map(x => x + 2)
Conversion between the two styles is possible via the wrap and unwrap operators
import * as M from 'jazzi/Maybe';
import * as F from 'jazzi/Maybe/fluent';
const pipeToFluent = F.wrap(M.of(41));
const fluentToPipe = F.of(41).unwrap();
Summary
From this point onwards, all imports are omitted
All structures have a toThenable
function that returns an object complies with the thenable interface. This allows for ease of composition with JS Promises and to be used with async/await syntax. In general, all happy cases return inner value, all adverse cases throw inner value. In versions <=2.2.1, the structure was a thenable with a then function. This caused some implicit behavior and the thenable feature was made opt-in with the toThenable
function.
const eitherLeftOrRight = Either.from(/*...*/);
const example = async () => {
try {
const x = await eitherLeftOrRight.toThenable()
console.log(`It was Right ${x}`);
} catch(e){
console.log(`It was Left ${e}`)
}
}
It also comes with a toPromise
function that converts any structure to a promise.
Maybe
Maybe represents the possibility of the absensce of value. Just
for values, None
for no values. The default constructor returns Just
of truthy values and None
otherwise. Unlike Either where Left
and Right
both behave like functors, None
does not behave like a functor, rather map
of a None
simply returns the structure unchanged.
Maybe.of(42) // Just 42
Maybe.of(false) // None
Either
Either represents a value that can be one of two possibilities, either Left
or Right
(badum tssss). By convention Left
is treated as the error case and Right
as the happy path but they are just two possibilities. The from
constructor receives a left value l
and right value r
where if r
is neither null nor undefined then we get Right r
otherwise we get a Left l
. There is a curried version of the fromFalsy
constructor that was made for convenience. The of
constructor is the same as calling the from
constructor but passing the same value as both arguments
const default42 = Either.defaultTo(42);
defaultTo42(undefined) // returns Left 42
defaultTo42(6 * 9) // returns Right (6 * 9)
const Right42 = Either.attempt(() => 42) // returns Right 42
const Left42 = Either.attempt(() => { throw 42 }) // returns Left 42
Async
The Async structure represents an async computation that may need some input. It can be seen as a mix between IO, Reader and a Promise. Unlike IO, run returns a promise with the result of the computation. Like IO, it is a lazy monad. Like Reader, the run method must receive the environment.
const s42 = Async.Success(42)
const f42 = Async.Fail(42)
await s42.run() // resolves with 42
await f42.run() // rejects with 42
const withEnv = Async.from(async (x) => x + 2)
await withEnv.run(40) // resolves with 42
// Environment can be provided using provide
const withEnvProvided = Async.from(async (x) => x + 2).provide(40)
await withEnvProvided.run() // resolves with 42
// Zip sequences two Asyncs and collects the result in a tuple. Left and Right variants discard one result.
// a.zip(b) is equivalent to a.flatMap(x => b.map(y => [x,y]))
await s42.zip(s42).run() // resolves to [42,42]
await s42.zipRight(Async.Success("right")).run() // resolves to "right"
await Async.Success("left").zipLeft(s42).run() // resolves to "left"
// The only way to recover from a Fail
await f42.recover((e) => Async.Success(e)).run() // resolves to 42
await Async.from(() => Promise.resolve(42)).run() // resolves to 42