rubico
Version:
[a]synchronous functional programming
210 lines (180 loc) • 5.04 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, tap) {
if (typeof module == 'object') (module.exports = tap) // CommonJS
else if (typeof define == 'function') define(() => tap) // AMD
else (root.tap = tap) // Browser
}(typeof globalThis == 'object' ? globalThis : this, (function () { 'use strict'
const isPromise = value => value != null && typeof value.then == 'function'
const always = value => function getter() { return value }
const thunkifyArgs = (func, args) => function thunk() {
return func(...args)
}
const thunkConditional = (
conditionalExpression, thunkOnTruthy, thunkOnFalsy,
) => conditionalExpression ? thunkOnTruthy() : thunkOnFalsy()
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)
}
// 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)
}
// argument resolver for curryArgs2
const curryArgs2ResolveArgs0 = (
baseFunc, arg1, arg2,
) => function args0Resolver(...args) {
return baseFunc(args, arg1)
}
// argument resolver for curryArgs2
const curryArgs2ResolveArgs1 = (
baseFunc, arg0, arg2,
) => function arg1Resolver(...args) {
return baseFunc(arg0, args)
}
const curryArgs2 = function (baseFunc, arg0, arg1) {
if (arg0 == __) {
return curryArgs2ResolveArgs0(baseFunc, arg1)
}
return curryArgs2ResolveArgs1(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)
}
const isArray = Array.isArray
const areAnyValuesPromises = function (values) {
if (isArray(values)) {
const length = values.length
let index = -1
while (++index < length) {
const value = values[index]
if (isPromise(value)) {
return true
}
}
return false
}
for (const key in values) {
const value = values[key]
if (isPromise(value)) {
return true
}
}
return false
}
const promiseAll = Promise.all.bind(Promise)
// _tap(args Array, f function) -> Promise|any
const _tap = function (args, f) {
const result = args[0],
call = f(...args)
return isPromise(call) ? call.then(always(result)) : result
}
const tap = function (...args) {
const f = args.pop()
if (args.length == 0) {
return curryArgs2(_tap, __, f)
}
if (areAnyValuesPromises(args)) {
return promiseAll(args).then(curry2(_tap, __, f))
}
return _tap(args, f)
}
const _tapIf = function (predicate, f, args) {
const b = predicate(...args)
if (isPromise(b)) {
return b.then(curry3(
thunkConditional,
__,
thunkifyArgs(tap(f), args),
always(args[0]),
))
}
if (b) {
const execution = f(...args)
if (isPromise(execution)) {
return execution.then(always(args[0]))
}
}
return args[0]
}
tap.if = function (...args) {
if (args.length == 2) {
return curryArgs3(_tapIf, args[0], args[1], __)
}
const argsLength = args.length
const f = args[argsLength - 1]
const predicate = args[argsLength - 2]
const argValues = args.slice(0, -2)
if (areAnyValuesPromises(argValues)) {
return promiseAll(argValues).then(curry3(_tapIf, predicate, f, __))
}
return _tapIf(predicate, f, args)
}
return tap
}())))