All files composition.js

94.19% Statements 81/86
93.75% Branches 15/16
100% Functions 33/33
96.3% Lines 52/54
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 1203x 3x 3x     6x 4x   2x       6x 5x   4x 4x 3x                         2x 16x 4x 16x     4x             3x 1x   3x 3x       3x 3x 2x 1x     1x               4x 3x   3x 3x   3x 4x 4x   4x               3x   2x     2x         2x 1x 1x       2x 1x 1x     3x 2x 1x 1x       9x 8x 5x   3x 3x 4x    
import { truthy, existy } from './predicates';
import { construct, cat } from './collections';
import { fail } from './util';
 
export function doWhen(cond, action) {
  if (truthy(cond))
    return action();
  else
    return undefined;
}
 
export function invoker(name, method) {
  return (target, ...args) => {
    if (!existy(target)) fail("Must provide a target");
 
    const targetMethod = target[name];
    return doWhen((existy(targetMethod) && method === targetMethod), () => {
      return targetMethod.apply(target, args);
    });
  }
}
 
// fnull takes a function as an arg and a number of additional args and returns
// a function that just calls the original function given.  If null/undefined
// are present it will replace them with some defaults:
//
// so lets say the fun passed in was (x, y) => x * y if we want to provide a
// default for x, y at any point it gets called we can add in two defaults for
// example: fnull(multiply, 1, 1); Now 1 will be replaced with x or y if they're
// ever undefined or null.
export function fnull(fun, ...defaults) {
  return function(...secondArgs) {
    const args = secondArgs.map((e, i) => {
      return existy(e) ? e : defaults[i];
    });
 
    return fun.apply(null, args);
  }
}
 
// Creates a predicate function to be used with 'checker' that conforms to its
// expected API of having a 'message' field.
export function validator(msg, fun) {
  const f = function(...args) {
    return fun.apply(fun, args);
  }
  f.message = msg;
  return f;
}
 
// checker can be used to do validation
export function checker(...validators) {
  return obj => {
    return validators.reduce((errs, check) => {
      Iif (check(obj)) {
        return errs;
      } else {
        return [...errs, check.message];
      }
    }, []);
  }
}
 
// The contract of dispatch: it will keep trying to execute functions until it
// runs out or one returns an existy value. 
export function dispatch(...funs) {
  const size = funs.length;
 
  return (target, ...args) => {
    let ret = undefined;
 
    for (let funIndex = 0; funIndex < size; funIndex++) {
      const fun = funs[funIndex];
      ret = fun.apply(fun, [target, ...args]);
 
      if (existy(ret)) return ret;
    }
 
    return ret;
  }
}
 
// Currying
export function curry(fun) { return arg => fun(arg); }
export function curry2(fun) {
  return secondArg => firstArg => fun(firstArg, secondArg);
}
export function curry3(fun) {
  return last => middle => first => fun(first, middle, last);
}
 
// Partial Application
export function partial1(fun, arg1) {
  return (...pargs) => {
    const args = construct(arg1, pargs)
    return fun.apply(fun, args)
  }
}
export function partial2(fun, arg1, arg2) {
  return (...pargs) => {
    const args = cat([arg1, arg2], pargs);
    return fun.apply(fun, args);
  }
}
export function partial(fun, ...pargs) {
  return (...internalArgs) => {
    const args = cat(pargs, internalArgs);
    return fun.apply(fun, args);
  }
}
 
export function compose(...funcs) {
  if (funcs.length === 0) return arg => arg
  if (funcs.length === 1) return funcs[0]
 
  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}