rubico
Version:
[a]synchronous functional programming
136 lines (129 loc) • 3.89 kB
JavaScript
const isPromise = require('./_internal/isPromise')
const always = require('./_internal/always')
const thunkifyArgs = require('./_internal/thunkifyArgs')
const thunkConditional = require('./_internal/thunkConditional')
const curry2 = require('./_internal/curry2')
const curry3 = require('./_internal/curry3')
const curryArgs2 = require('./_internal/curryArgs2')
const curryArgs3 = require('./_internal/curryArgs3')
const __ = require('./_internal/placeholder')
const areAnyValuesPromises = require('./_internal/areAnyValuesPromises')
const promiseAll = require('./_internal/promiseAll')
// _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
}
/**
* @name tap
*
* @synopsis
* ```coffeescript [specscript]
* tap(...args, f function) -> Promise|args[0]
* tap(f function)(...args) -> Promise|args[0]
* ```
*
* @description
* Call a function with provided arguments, returning the first argument. The return value of the function call is discarded.
*
* ```javascript [playground]
* const pipeline = pipe([
* tap(value => console.log(value)),
* tap(value => console.log(value + 'bar')),
* tap(value => console.log(value + 'barbaz')),
* ])
*
* pipeline('foo') // 'foo'
* // 'foobar'
* // 'foobarbaz'
* ```
*
* Any promises passed in argument position are resolved for their values before further execution. This only applies to the eager version of the API.
*
* ```javascript [playground]
* tap(Promise.resolve(1), Promise.resolve(2), 3, console.log) // 1 2 3
* ```
*/
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)
}
/**
* @name _tapIf
*
* @synopsis
* ```coffeescript [specscript]
* _tapIf(
* predicate function,
* f function,
* args Array,
* ) -> Promise|args[0]
* ```
*/
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]
}
/**
* @name tap.if
*
* @synopsis
* ```coffeescript [specscript]
* tap.if(...args, predicate function, f function) -> Promise|args[0]
* tap.if(predicate function, f function)(...args) -> Promise|args[0]
* ```
*
* @description
* A version of `tap` that accepts a predicate function (a function that returns a boolean value) before the function `f` to execute. Only executes `f` if the predicate function tests true. The arguments are the same to both the predicate function and the function to execute `f`.
*
* ```javascript [playground]
* const isOdd = number => number % 2 == 1
*
* const logIfOdd = tap.if(isOdd, console.log)
*
* logIfOdd(2)
* logIfOdd(3) // 3
* ```
*
* Any promises passed in argument position are resolved for their values before further execution. This only applies to the eager version of the API.
*
* ```javascript [playground]
* tap.if(Promise.resolve(1), n => n < 5, console.log) // 1
* tap.if(Promise.resolve(6), n => n < 5, console.log)
* ```
*/
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)
}
module.exports = tap