Curry Functions With Placeholders

Transform multi-argument functions into curried variants that support placeholders and partial application.

JavascriptData Integrity

Code

snippet
export const curryPlaceholder = Symbol("curry.placeholder");

export function curry(fn, arity = fn.length) {
  if (typeof fn !== "function") throw new TypeError("fn must be a function");
  if (!Number.isInteger(arity) || arity < 0) throw new RangeError("arity must be a non-negative integer");
  return function curried(...args) {
    const supplied = args.slice(0, arity);
    if (supplied.length === arity && !supplied.includes(curryPlaceholder)) {
      return fn.apply(this, supplied);
    }
    return function next(...more) {
      const merged = [];
      let cursor = 0;
      for (const argument of args) {
        if (argument === curryPlaceholder && cursor < more.length) {
          merged.push(more[cursor]);
          cursor += 1;
        } else {
          merged.push(argument);
        }
      }
      while (cursor < more.length) {
        merged.push(more[cursor]);
        cursor += 1;
      }
      return curried.apply(this, merged);
    };
  };
}

export function uncurry(curried) {
  if (typeof curried !== "function") throw new TypeError("curried must be a function");
  return (...args) => args.reduce((fn, argument) => fn(argument), curried);
}