Hello, this is a synchronous continuation monad library, with the following goals:
- Promise compatibility
- Flexibility
- Usability
Think of this as a synchronous Promise-API if it helps
Here is a basic Promise-compatible example to let you know what this library can do for you:
Contract.resolve('He') // Contract { 'He' }
.then(x => x + 'llo') // Contract { 'Hello' }
.then(x => x.split('')) // Contract { [ 'H', 'e', 'l', 'l', 'o' ] }
.map(x => x.charCodeAt(0)) // Contract { [ 72, 101, 108, 108, 111 ] }
.map(x => x * 3) // Contract { [ 216, 303, 324, 324, 333 ] }
.filter(x => x % 2 === 0) // Contract { [ 216, 324, 324 ] }
.tap(console.log) // Contract { [ 216, 324, 324 ] }
// Logs: [ 216, 324, 324 ]
.reduce((a, b) => a + b, 0) // Contract { 864 }
.then(() => {
throw new Error('Hello World');
}) // Contract { (rejected) Error<Hello World> }
.catch(console.error); // Contract { undefined }
It also supports laziness through generators!
function* naturalNumbers() {
let i = 1;
while (true)
yield i++;
}
Contract.of(naturalNumbers())
.lazy.map(x => x * 3)
.lazy.filter(x => x % 2 === 0)
.lazy.take(10)
.toArray()
.then(console.log);
// Logs: [ 6, 12, 18, 24, 30, 36, 42, 48, 54, 60 ]
(This will only start iterating natural numbers after .toArray()
has been called)
npm i @thedevs/contract
- Make this library fully Promise-compatible (except async stuff)
- Make it work in browsers
- Have examples for all methods
@MKRhere for the original idea
This is the complete function signature of the constructor:
Contract(Contract | Promise | Function -> (Contract | Promise | any) | any[, ...args])
-> Contract { any } | Promise { any }
Expressed in english:
- If the first argument is already a
Contract
, it will simply return that. - If the first argument is a
Promise
, it will return that - If the first argument is not a function, it will return a
Contract
that resolves to that - Otherwise, it will run the function with the provided rest arguments.
- If the function throws, it will return a rejecting
Contract
- If the return value of the function is a
Contract
or aPromise
, it will return that - Otherwise, it will return
Contract { return value }
Aliases, these three do the same: Create a contract from a value
Contract.resolve(value) -> Contract { value }
Create a rejecting Contract from the given error (or value)
Checks if the given value is a Contract
Resolves an array of functions, values, or Contracts (rejects if any of the functions throw, return a rejecting Contract, or any Contract in the array is rejected)
All instance methods are also available as thunks, for Promise-interoperability:
Promise.resolve(Contract.of([ 1, 2, 3 ]))
.then(Contract.map(x => x * 2))
.then(Contract.reduce((a, b) => a + b, 0))
.then(Contract.tap(console.log))
// Promise { Contract { 12 } }
Works just like Promises, maps the existing value to a new value
Catches a rejected Contract (Resolves it)
Do something with a value without changing the Contract (useful for logging / debugging)
Map the contents of an array within a Contract easily (Similar to bluebird's map)
Similar to bluebird's reduce
Similar to bluebird's filter
Similar to bluebird's spread
Slice an array within a contract (take the first N elements)
Convert the inner value of a contract to an array (useful with lazy values)
Convert the inner value of a contract to a string
Lazily map values (Returns an iterator)
Lazily filter values (Returns an iterator)
Lazily take values (Returns an iterator)