Instantly share code, notes, and snippets.

Embed
What would you like to do?
Simple and small wrapper to allow for partial function invocation
/**
* Make a function partially invokable, meaning that its
* arguments can be provided in any order.
*
* @example
* let addThreeNumbers = partial((a, b, c) => a + b + c);
* let plus4 = addThreeNumbers(1, _, 3);
* plus4(4); // => 8
*
* @param {Function} fn The function to apply partial invokability to.
* @return {Function} A partially invokable function.
* @license The-Unlicense
*/
function partial(fn) {
// Check if the provided argument is a function
if (typeof fn !== 'function') throw Error('Partial should be provided with a function');
// Construct the initial memory
const arity = fn.length;
const allPlaceholders = [...Array(arity)].map(() => _);
// Return the function returned by the resolver. Where the
// resolver manages the memory of a partially invoked function
return (function resolver() {
// Create *local* memory to allow for reuse of a partially invoked function
const memory = Array.from(arguments);
return function() {
let args = memory.slice(0);
// Replace placeholder values with actual arguments from left to right
let j = 0;
for (let [i, arg] of args.entries()) {
if (arg === _) args[i] = arguments[j++];
}
// Call fn if there is no more placeholder values, otherwise return
// another partially invoked function
const next = args.includes(_) ? resolver : fn;
return next.apply(null, args);
};
}.apply(null, allPlaceholders));
}
/* Variable to denote an argument placeholder, inspired by Scala */
const _ = new class PartialPlaceholder { }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment