rubico
Version:
[a]synchronous functional programming
569 lines (491 loc) • 15.4 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.
*/
(function (root, transform) {
if (typeof module == 'object') (module.exports = transform) // CommonJS
else if (typeof define == 'function') define(() => transform) // AMD
else (root.transform = transform) // Browser
}(typeof globalThis == 'object' ? globalThis : this, (function () { 'use strict'
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 isBinary = ArrayBuffer.isView
const callPropUnary = (value, property, arg0) => value[property](arg0)
// 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)
}
const always = value => function getter() { return value }
const noop = function () {}
const add = (a, b) => a + b
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 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)
}
const objectAssign = Object.assign
const _arrayExtend = function (array, values) {
const arrayLength = array.length,
valuesLength = values.length
let valuesIndex = -1
while (++valuesIndex < valuesLength) {
array[arrayLength + valuesIndex] = values[valuesIndex]
}
return array
}
const arrayExtend = function (array, values) {
if (isArray(values) || isBinary(values)) {
return _arrayExtend(array, values)
}
array.push(values)
return array
}
const globalThisHasBuffer = typeof Buffer == 'function'
const bufferAlloc = globalThisHasBuffer ? Buffer.alloc : noop
const _binaryExtend = function (typedArray, array) {
const offset = typedArray.length
const result = globalThisHasBuffer && typedArray.constructor == Buffer
? bufferAlloc(offset + array.length)
: new typedArray.constructor(offset + array.length)
result.set(typedArray)
result.set(array, offset)
return result
}
const binaryExtend = function (typedArray, array) {
if (isArray(array) || isBinary(array)) {
return _binaryExtend(typedArray, array)
}
return _binaryExtend(typedArray, [array])
}
const thunkify1 = (func, arg0) => function thunk() {
return func(arg0)
}
const isNodeReadStream = value => value != null && typeof value.pipe == 'function'
const __streamWrite = stream => function appender(
chunk, encoding, callback,
) {
stream.write(chunk, encoding, callback)
return stream
}
const _streamExtendExecutor = (
resultStream, stream,
) => function executor(resolve, reject) {
stream.on('data', __streamWrite(resultStream))
stream.on('end', thunkify1(resolve, resultStream))
stream.on('error', reject)
}
const _streamExtend = (
resultStream, stream,
) => new Promise(_streamExtendExecutor(resultStream, stream))
const streamExtend = function (stream, values) {
if (isNodeReadStream(values)) {
return _streamExtend(stream, values)
}
stream.write(values)
return stream
}
const setExtend = function (set, values) {
if (values != null && values.constructor == Set) {
for (const value of values) {
set.add(value)
}
return set
}
return set.add(values)
}
const callConcat = function (object, values) {
return object.concat(values)
}
const identityTransform = function (collection, transducer, accum) {
const nil = genericReduce(collection, transducer(noop), null)
return isPromise(nil) ? nil.then(always(accum)) : accum
}
const genericTransform = function (collection, transducer, accum) {
if (isArray(accum)) {
return genericReduce(collection, transducer(arrayExtend), accum)
}
if (isBinary(accum)) {
const intermediateArray = genericReduce(collection, transducer(arrayExtend), [])
return isPromise(intermediateArray)
? intermediateArray.then(curry2(binaryExtend, accum, __))
: binaryExtend(accum, intermediateArray)
}
if (accum == null) {
return identityTransform(collection, transducer, accum)
}
const constructor = accum.constructor
if (typeof accum == 'string' || constructor == String) {
const result = genericReduce(collection, transducer(arrayExtend), [accum])
return isPromise(result)
? result.then(curry3(callPropUnary, __, 'join', ''))
: result.join('')
}
if (typeof accum.concat == 'function') {
return genericReduce(collection, transducer(callConcat), accum)
}
if (typeof accum.write == 'function') {
return genericReduce(collection, transducer(streamExtend), accum)
}
if (constructor == Set) {
return genericReduce(collection, transducer(setExtend), accum)
}
if (constructor == Object) {
return genericReduce(collection, transducer(objectAssign), accum)
}
return identityTransform(collection, transducer, accum)
}
// _transform(collection any, transducer function, initialValue function|any) -> Promise
const _transform = function (collection, transducer, initialValue) {
if (typeof initialValue == 'function') {
const actualInitialValue = initialValue(collection)
return isPromise(actualInitialValue)
? actualInitialValue.then(curry3(genericTransform, collection, transducer, __))
: genericTransform(collection, transducer, actualInitialValue)
}
return isPromise(initialValue)
? initialValue.then(curry3(genericTransform, collection, transducer, __))
: genericTransform(collection, transducer, initialValue)
}
const transform = function (...args) {
if (typeof args[0] == 'function') {
return curry3(_transform, __, args[0], args[1])
}
if (isPromise(args[0])) {
return args[0].then(curry3(_transform, __, args[1], args[2]))
}
return _transform(args[0], args[1], args[2])
}
return transform
}())))