rubico
Version:
[a]synchronous functional programming
322 lines (283 loc) • 8.29 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 __ = Symbol.for('placeholder')
// 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 symbolIterator = Symbol.iterator
const FilteringIterator = (iterator, predicate) => ({
[symbolIterator]() {
return this
},
next() {
let iteration = iterator.next()
while (!iteration.done) {
const { value } = iteration
if (predicate(value)) {
return { value, done: false }
}
iteration = iterator.next()
}
return iteration
},
})
const isPromise = value => value != null && typeof value.then == 'function'
const symbolAsyncIterator = Symbol.asyncIterator
const FilteringAsyncIterator = (asyncIterator, predicate) => ({
isAsyncIteratorDone: false,
[symbolAsyncIterator]() {
return this
},
async next() {
while (!this.isAsyncIteratorDone) {
const { value, done } = await asyncIterator.next()
if (done) {
this.isAsyncIteratorDone = true
} else {
const predication = predicate(value)
if (isPromise(predication) ? await predication : predication) {
return { value, done: false }
}
}
}
return { value: undefined, done: true }
},
})
const isArray = Array.isArray
// 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 promiseAll = Promise.all.bind(Promise)
const arrayExtendMap = function (
array, values, valuesMapper, valuesIndex,
) {
const valuesLength = values.length
let arrayIndex = array.length - 1
while (++valuesIndex < valuesLength) {
array[++arrayIndex] = valuesMapper(values[valuesIndex], valuesIndex, array)
}
return array
}
const arrayFilterByConditions = function (
array, result, index, conditions,
) {
const arrayLength = array.length
let conditionsIndex = -1
while (++index < arrayLength) {
if (conditions[++conditionsIndex]) {
result.push(array[index])
}
}
return result
}
const arrayFilter = function (array, predicate) {
const arrayLength = array.length,
result = []
let index = -1,
resultIndex = -1
while (++index < arrayLength) {
const item = array[index],
shouldIncludeItem = predicate(item, index, array)
if (isPromise(shouldIncludeItem)) {
return promiseAll(
arrayExtendMap([shouldIncludeItem], array, predicate, index)
).then(curry4(arrayFilterByConditions, array, result, index - 1, __))
}
if (shouldIncludeItem) {
result[++resultIndex] = item
}
}
return result
}
// 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 callPropUnary = (value, property, arg0) => value[property](arg0)
const stringFilter = function (string, predicate) {
const filteredCharactersArray = arrayFilter(string, predicate)
return isPromise(filteredCharactersArray)
? filteredCharactersArray.then(curry3(callPropUnary, __, 'join', ''))
: filteredCharactersArray.join('')
}
const always = value => function getter() { return value }
const thunkConditional = (
conditionalExpression, thunkOnTruthy, thunkOnFalsy,
) => conditionalExpression ? thunkOnTruthy() : thunkOnFalsy()
const thunkify1 = (func, arg0) => function thunk() {
return func(arg0)
}
const noop = function () {}
const setFilter = function (value, predicate) {
const result = new Set(),
resultAdd = result.add.bind(result),
promises = []
for (const item of value) {
const predication = predicate(item, item, value)
if (isPromise(predication)) {
promises.push(predication.then(curry3(
thunkConditional, __, thunkify1(resultAdd, item), noop)))
} else if (predication) {
result.add(item)
}
}
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
const thunkify4 = (func, arg0, arg1, arg2, arg3) => function thunk() {
return func(arg0, arg1, arg2, arg3)
}
const callPropBinary = (value, property, arg0, arg1) => value[property](arg0, arg1)
const mapFilter = function (map, predicate) {
const result = new Map(),
promises = []
for (const [key, item] of map) {
const predication = predicate(item, key, map)
if (isPromise(predication)) {
promises.push(predication.then(curry3(thunkConditional,
__,
thunkify4(callPropBinary, result, 'set', key, item),
noop)))
} else if (predication) {
result.set(key, item)
}
}
return promises.length == 0 ? result
: promiseAll(promises).then(always(result))
}
const objectSetIf = function (
object, key, value, condition,
) {
if (condition) {
object[key] = value
}
}
const objectFilter = function (object, predicate) {
const result = {},
promises = []
for (const key in object) {
const item = object[key],
shouldIncludeItem = predicate(item, key, object)
if (isPromise(shouldIncludeItem)) {
promises.push(shouldIncludeItem.then(
curry4(objectSetIf, result, key, object[key], __)))
} else if (shouldIncludeItem) {
result[key] = item
}
}
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
const _filter = function (value, predicate) {
if (isArray(value)) {
return arrayFilter(value, predicate)
}
if (value == null) {
return value
}
if (typeof value == 'string' || value.constructor == String) {
return stringFilter(value, predicate)
}
if (value.constructor == Set) {
return setFilter(value, predicate)
}
if (value.constructor == Map) {
return mapFilter(value, predicate)
}
if (typeof value.filter == 'function') {
return value.filter(predicate)
}
if (typeof value[symbolIterator] == 'function') {
return FilteringIterator(value[symbolIterator](), predicate)
}
if (typeof value[symbolAsyncIterator] == 'function') {
return FilteringAsyncIterator(value[symbolAsyncIterator](), predicate)
}
if (value.constructor == Object) {
return objectFilter(value, predicate)
}
return value
}
const filter = function (arg0, arg1) {
if (typeof arg0 == 'function') {
return curry2(_filter, __, arg0)
}
return _filter(arg0, arg1)
}
export default filter