rubico
Version:
[a]synchronous functional programming
423 lines (369 loc) • 11.2 kB
JavaScript
/**
* rubico v2.6.2
* https://github.com/a-synchronous/rubico
* (c) 2019-2024 Richard Tong
* rubico may be freely distributed under the MIT license.
*/
const isPromise = value => value != null && typeof value.then == 'function'
const __ = Symbol.for('placeholder')
// argument resolver for curry3
const curry3ResolveArg0 = (
baseFunc, arg1, arg2,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1, arg2)
}
// argument resolver for curry3
const curry3ResolveArg1 = (
baseFunc, arg0, arg2,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1, arg2)
}
// argument resolver for curry3
const curry3ResolveArg2 = (
baseFunc, arg0, arg1,
) => function arg2Resolver(arg2) {
return baseFunc(arg0, arg1, arg2)
}
const curry3 = function (baseFunc, arg0, arg1, arg2) {
if (arg0 == __) {
return curry3ResolveArg0(baseFunc, arg1, arg2)
}
if (arg1 == __) {
return curry3ResolveArg1(baseFunc, arg0, arg2)
}
return curry3ResolveArg2(baseFunc, arg0, arg1)
}
const isArray = Array.isArray
const objectValues = Object.values
const objectProto = Object.prototype
const nativeObjectToString = objectProto.toString
const objectToString = value => nativeObjectToString.call(value)
const generatorFunctionTag = '[object GeneratorFunction]'
const isGeneratorFunction = value => objectToString(value) == generatorFunctionTag
const asyncGeneratorFunctionTag = '[object AsyncGeneratorFunction]'
const isAsyncGeneratorFunction = value => objectToString(value) == asyncGeneratorFunctionTag
const iteratorReduceAsync = async function (
iterator, reducer, result,
) {
let iteration = iterator.next()
if (iteration.done) {
return result
}
while (!iteration.done) {
result = reducer(result, iteration.value)
if (isPromise(result)) {
result = await result
}
iteration = iterator.next()
}
return result
}
const iteratorReduce = function (iterator, reducer, result) {
let iteration = iterator.next()
if (iteration.done) {
return result
}
if (result === undefined) {
result = iteration.value
iteration = iterator.next()
}
while (!iteration.done) {
result = reducer(result, iteration.value)
if (isPromise(result)) {
return result.then(curry3(iteratorReduceAsync, iterator, reducer, __))
}
iteration = iterator.next()
}
return result
}
const asyncIteratorReduce = async function (asyncIterator, reducer, result) {
let iteration = await asyncIterator.next()
if (iteration.done) {
return result
}
if (result === undefined) {
result = iteration.value
iteration = await asyncIterator.next()
}
while (!iteration.done) {
result = await reducer(result, iteration.value)
iteration = await asyncIterator.next()
}
return result
}
const symbolIterator = Symbol.iterator
const symbolAsyncIterator = Symbol.asyncIterator
// argument resolver for curry2
const curry2ResolveArg0 = (
baseFunc, arg1,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1)
}
// argument resolver for curry2
const curry2ResolveArg1 = (
baseFunc, arg0,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1)
}
const curry2 = function (baseFunc, arg0, arg1) {
return arg0 == __
? curry2ResolveArg0(baseFunc, arg1)
: curry2ResolveArg1(baseFunc, arg0)
}
// argument resolver for curryArgs3
const curryArgs3ResolveArgs0 = (
baseFunc, arg1, arg2,
) => function args0Resolver(...args) {
return baseFunc(args, arg1, arg2)
}
// argument resolver for curryArgs3
const curryArgs3ResolveArgs1 = (
baseFunc, arg0, arg2,
) => function arg1Resolver(...args) {
return baseFunc(arg0, args, arg2)
}
// argument resolver for curryArgs3
const curryArgs3ResolveArgs2 = (
baseFunc, arg0, arg1,
) => function arg2Resolver(...args) {
return baseFunc(arg0, arg1, args)
}
const curryArgs3 = function (baseFunc, arg0, arg1, arg2) {
if (arg0 == __) {
return curryArgs3ResolveArgs0(baseFunc, arg1, arg2)
}
if (arg1 == __) {
return curryArgs3ResolveArgs1(baseFunc, arg0, arg2)
}
return curryArgs3ResolveArgs2(baseFunc, arg0, arg1)
}
// argument resolver for curry4
const curry4ResolveArg0 = (
baseFunc, arg1, arg2, arg3,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1, arg2, arg3)
}
// argument resolver for curry4
const curry4ResolveArg1 = (
baseFunc, arg0, arg2, arg3,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1, arg2, arg3)
}
// argument resolver for curry4
const curry4ResolveArg2 = (
baseFunc, arg0, arg1, arg3,
) => function arg2Resolver(arg2) {
return baseFunc(arg0, arg1, arg2, arg3)
}
// argument resolver for curry4
const curry4ResolveArg3 = (
baseFunc, arg0, arg1, arg2,
) => function arg3Resolver(arg3) {
return baseFunc(arg0, arg1, arg2, arg3)
}
const curry4 = function (baseFunc, arg0, arg1, arg2, arg3) {
if (arg0 == __) {
return curry4ResolveArg0(baseFunc, arg1, arg2, arg3)
}
if (arg1 == __) {
return curry4ResolveArg1(baseFunc, arg0, arg2, arg3)
}
if (arg2 == __) {
return curry4ResolveArg2(baseFunc, arg0, arg1, arg3)
}
return curry4ResolveArg3(baseFunc, arg0, arg1, arg2)
}
const arrayReduceAsync = async function (
array, reducer, result, index,
) {
const length = array.length
while (++index < length) {
result = reducer(result, array[index], index, array)
if (isPromise(result)) {
result = await result
}
}
return result
}
const arrayReduce = function (array, reducer, result) {
const arrayLength = array.length
let index = -1
if (result === undefined) {
result = array[++index]
}
while (++index < arrayLength) {
result = reducer(result, array[index], index, array)
if (isPromise(result)) {
return result.then(curry4(arrayReduceAsync, array, reducer, __, index))
}
}
return result
}
// argument resolver for curry5
const curry5ResolveArg0 = (
baseFunc, arg1, arg2, arg3, arg4,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg1 = (
baseFunc, arg0, arg2, arg3, arg4,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg2 = (
baseFunc, arg0, arg1, arg3, arg4,
) => function arg2Resolver(arg2) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg3 = (
baseFunc, arg0, arg1, arg2, arg4,
) => function arg3Resolver(arg3) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg4 = (
baseFunc, arg0, arg1, arg2, arg3,
) => function arg3Resolver(arg4) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
const curry5 = function (baseFunc, arg0, arg1, arg2, arg3, arg4) {
if (arg0 == __) {
return curry5ResolveArg0(baseFunc, arg1, arg2, arg3, arg4)
}
if (arg1 == __) {
return curry5ResolveArg1(baseFunc, arg0, arg2, arg3, arg4)
}
if (arg2 == __) {
return curry5ResolveArg2(baseFunc, arg0, arg1, arg3, arg4)
}
if (arg3 == __) {
return curry5ResolveArg3(baseFunc, arg0, arg1, arg2, arg4)
}
return curry5ResolveArg4(baseFunc, arg0, arg1, arg2, arg3)
}
const objectKeys = Object.keys
const objectReduceAsync = async function (object, reducer, result, keys, index) {
const keysLength = keys.length
while (++index < keysLength) {
const key = keys[index]
result = reducer(result, object[key], key, object)
if (isPromise(result)) {
result = await result
}
}
return result
}
const objectReduce = function (object, reducer, result) {
const keys = objectKeys(object),
keysLength = keys.length
let index = -1
if (result === undefined) {
result = object[keys[++index]]
}
while (++index < keysLength) {
const key = keys[index]
result = reducer(result, object[key], key, object)
if (isPromise(result)) {
return result.then(curry5(objectReduceAsync, object, reducer, __, keys, index))
}
}
return result
}
const mapReduceAsync = async function (
map, reducer, result, mapEntriesIter,
) {
for (const [key, value] of mapEntriesIter) {
result = reducer(result, value, key, map)
if (isPromise(result)) {
result = await result
}
}
return result
}
const mapReduce = function (map, reducer, result) {
const mapEntriesIter = map.entries()
if (result === undefined) {
const firstIteration = mapEntriesIter.next()
if (firstIteration.done) {
return result
}
result = firstIteration.value[1]
}
for (const [key, value] of mapEntriesIter) {
result = reducer(result, value, key, map)
if (isPromise(result)) {
return result.then(curry4(
mapReduceAsync, map, reducer, __, mapEntriesIter))
}
}
return result
}
const reducerConcat = (
reducerA, reducerB,
) => function pipedReducer(result, item) {
const intermediate = reducerA(result, item)
return isPromise(intermediate)
? intermediate.then(curry2(reducerB, __, item))
: reducerB(intermediate, item)
}
const genericReduce = function (collection, reducer, result) {
if (isArray(collection)) {
return arrayReduce(collection, reducer, result)
}
if (collection == null) {
return result === undefined
? curry2(reducer, collection, __)
: reducer(result, collection)
}
if (collection.constructor == Map) {
return mapReduce(collection, reducer, result)
}
if (typeof collection[symbolIterator] == 'function') {
return iteratorReduce(
collection[symbolIterator](), reducer, result)
}
if (typeof collection[symbolAsyncIterator] == 'function') {
return asyncIteratorReduce(
collection[symbolAsyncIterator](), reducer, result)
}
if (typeof collection.reduce == 'function') {
return collection.reduce(reducer, result)
}
if (typeof collection.chain == 'function') {
return collection.chain(curry2(reducer, result, __))
}
if (typeof collection.flatMap == 'function') {
return collection.flatMap(curry2(reducer, result, __))
}
if (collection.constructor == Object) {
return objectReduce(collection, reducer, result)
}
return result === undefined
? curry2(reducer, collection, __)
: reducer(result, collection)
}
// _reduce(collection any, reducer function, initialValue function|any) -> Promise
const _reduce = function (collection, reducer, initialValue) {
if (typeof initialValue == 'function') {
const actualInitialValue = initialValue(collection)
return isPromise(actualInitialValue)
? actualInitialValue.then(curry3(genericReduce, collection, reducer, __))
: genericReduce(collection, reducer, actualInitialValue)
}
return isPromise(initialValue)
? initialValue.then(curry3(genericReduce, collection, reducer, __))
: genericReduce(collection, reducer, initialValue)
}
const reduce = function (...args) {
if (typeof args[0] == 'function') {
return curry3(_reduce, __, args[0], args[1])
}
if (isPromise(args[0])) {
return args[0].then(curry3(_reduce, __, args[1], args[2]))
}
return _reduce(args[0], args[1], args[2])
}
export default reduce