UNPKG

stick-js

Version:

Fast toolkit for functional programming in JS. Provides idioms for referentially transparent expressions, clear separation of mutable and immutable operations, object factories, function calls based on English grammar, and pipe & compose operators.

328 lines (321 loc) 31.9 kB
/* As a general guideline we try to keep the functions here as fast as possible, even at the cost of code duplication. */import sprintfMod from'sprintf-js';const{sprintf}=sprintfMod;import{oStr,hasOwnProperty,safeObject}from"./internal.mjs";// --- @future curry these? export const pipe=(a,b)=>b(a);export const composeRight=(a,b)=>(...args)=>b(a(...args));export const compose=(a,b)=>(...args)=>a(b(...args));// --- @experimental export const composeAsMethodsRight=(b,a)=>a.compose(b);export const composeAsMethods=(a,b)=>a.compose(b);export const noop=()=>{};export const not=x=>!x;export const ok=x=>x!=null;export const notOk=x=>x==null;export const always=x=>_=>x;export const eq=x=>y=>x===y;export const ne=x=>y=>x!==y;export const gt=m=>n=>n>m;export const gte=m=>n=>n>=m;export const lt=m=>n=>n<m;export const lte=m=>n=>n<=m;/* Matches on exactly `true` or `false` */export const isTrue=eq(true);export const isFalse=eq(false);export const ifPredicate=p=>yes=>no=>x=>p(x)?yes(x):no(x);export const whenPredicate=p=>yes=>ifPredicate(p)(yes)(noop);export const ifPredicateV=p=>yes=>no=>x=>p(x)?yes:no;export const whenPredicateV=p=>yes=>ifPredicateV(p)(yes)(void 8);/* These are like `ifPredicate`/`whenPredicate`, but pass the result of the predicate test to the * condition functions. */export const ifPredicateResults=p=>yes=>no=>x=>{const y=p(x);return y?yes(y):no(y);};export const whenPredicateResults=p=>yes=>ifPredicateResults(p)(yes)(noop);/* These are like `ifPredicate`/`whenPredicate`, but pass both `x` and the result of the predicate * test to the condition functions. */export const ifPredicateWithResults=p=>yes=>no=>x=>{const y=p(x);return y?yes(x,y):no(x,y);};export const whenPredicateWithResults=p=>yes=>ifPredicateWithResults(p)(yes)(noop);/* * An anaphoric if which branches on a constant condition. The piped argument is ignored when * deciding the condition, but is passed to the branching functions. * * Usage: * res | ifAlways (someConstantCondition) ( res => yes ... res => no ... ) * * This is less lazy in its evaluation of the condition than the normal if/whenPredicate functions. To retain the laziness, use if/whenPredicate, and ignore the argument: * * res | ifPredicate (() => someConstantCondition) (...) * * Derivation: * const isAlways = x => _ => x * const ifAlways = x => isAlways (x) | ifPredicate * const isAlways = always * const ifAlways = x => always (x) | ifPredicate * const ifAlways = always >> ifPredicate */export const ifAlways=composeRight(always,ifPredicate);export const whenAlways=composeRight(always,whenPredicate);/* Truthy / falsey */export const isYes=Boolean;export const isNo=not;// --- @future whenNil, ifNil // ------ types. export const getType=x=>oStr.call(x).slice(8,-1);export const isType=t=>x=>getType(x)===t;// --- Proxy counts as a function (it's how lodash does it). export const isFunction=o=>{const type=getType(o);return false||type==='Function'||type==='GeneratorFunction'||type==='AsyncFunction'||type==='Proxy';};export const isArray=/*#__PURE__*/isType('Array');export const isObject=/*#__PURE__*/isType('Object');export const isNumber=/*#__PURE__*/isType('Number');export const isRegExp=/*#__PURE__*/isType('RegExp');export const isBoolean=/*#__PURE__*/isType('Boolean');export const isString=/*#__PURE__*/isType('String');export const isSymbol=/*#__PURE__*/isType('Symbol');export const isDate=/*#__PURE__*/isType('Date');export const isSet=/*#__PURE__*/isType('Set');export const isMap=/*#__PURE__*/isType('Map');export const tap=f=>o=>(f(o),o);export const dot=prop=>o=>o[prop]();export const dot1=prop=>val=>o=>o[prop](val);export const dot2=prop=>val1=>val2=>o=>o[prop](val1,val2);export const dot3=prop=>val1=>val2=>val3=>o=>o[prop](val1,val2,val3);export const dot4=prop=>val1=>val2=>val3=>val4=>o=>o[prop](val1,val2,val3,val4);export const dot5=prop=>val1=>val2=>val3=>val4=>val5=>o=>o[prop](val1,val2,val3,val4,val5);export const dotN=prop=>vs=>o=>o[prop](...vs);export const side=prop=>o=>(dot(prop)(o),o);export const side1=prop=>val1=>o=>(dot1(prop)(val1)(o),o);export const side2=prop=>val1=>val2=>o=>(dot2(prop)(val1)(val2)(o),o);export const side3=prop=>val1=>val2=>val3=>o=>(dot3(prop)(val1)(val2)(val3)(o),o);export const side4=prop=>val1=>val2=>val3=>val4=>o=>(dot4(prop)(val1)(val2)(val3)(val4)(o),o);export const side5=prop=>val1=>val2=>val3=>val4=>val5=>o=>(dot5(prop)(val1)(val2)(val3)(val4)(val5)(o),o);export const sideN=prop=>vs=>o=>(dotN(prop)(vs)(o),o);// --- @future reversed version of `has. /* These all ignore symbol keys. */export const has=k=>o=>hasOwnProperty.call(o,k);export const hasIn=k=>o=>k in o;export const ifHas=yes=>no=>([o,k])=>has(k)(o)?yes(o[k],o,k):no(o,k);export const whenHas=yes=>ifHas(yes)(noop);export const ifHasIn=yes=>no=>([o,k])=>hasIn(k)(o)?yes(o[k],o,k):no(o,k);export const whenHasIn=yes=>ifHasIn(yes)(noop);export const bindLatePropTo=o=>prop=>(...args)=>o[prop](...args);export const bindLateProp=prop=>o=>(...args)=>o[prop](...args);export const bindPropTo=o=>prop=>o[prop].bind(o);export const bindProp=prop=>o=>o[prop].bind(o);export const bindTo=o=>f=>f.bind(o);export const bind=f=>o=>f.bind(o);// --- bindTry* returns `null` if o[prop] is not a function. export const bindTryPropTo=o=>prop=>typeof o[prop]==='function'?bindPropTo(o)(prop):null;export const bindTryProp=prop=>o=>typeof o[prop]==='function'?bindPropTo(o)(prop):null;export const bindTryTo=o=>f=>typeof f==='function'?bindTo(o)(f):null;export const bindTry=f=>o=>typeof f==='function'?bindTo(o)(f):null;// --- @experimental // --- @todo this is not that nice to use. // --- trier is e.g. `bindTryPropTo` export const ifBind=trier=>ifPredicateWithResults(passToN(trier));export const whenBind=trier=>yes=>ifBind(trier)(yes)(noop);/* Usage * cond ( * [_ => 3 == 4, _ => 'twilight zone'], * [_ => 3 == 5, _ => 'even stranger'], * [T, _ => 'ok'], * ) * * or with a native idiom: * * cond ( * (_ => 3 == 4) | guard (_ => 'twilight zone'), * (_ => 3 == 5) | guard (_ => 'even stranger'), * otherwise | guard (_ => 'ok'), * ) * * `guardV` is a convenience for a guard which returns a simple expression, so guard (_ => 'twilight zone') * could be replaced by guardV ('twilight zone') * * The advantage of the left side being functions is that they are late-evaluated, they stop as soon * as one matches, and they can interact with a piped argument (see `condS` below. * * If you want simple, eager constant conditions on the left, just use a table :) * */export const T=always(true);export const F=always(false);export const condPredicate=exec=>pred=>[pred,exec];export const condPredicateDiscard=exec=>pred=>[pred,(target,_result)=>exec(target)];/* * Tests on truthiness, not strict equality. * This is how `if` works, it's how `cond` works in Ramda, and it's trivial to convert truthy to * strict. */export const cond=(...blocks)=>{for(const[test,exec]of blocks){const result=test();if(result)return exec(result);}};export const condS=blocks=>target=>{for(const[test,exec]of blocks){const result=test(target);if(result)return exec(target,result);}};export const add=m=>n=>m+n;export const multiply=m=>n=>m*n;export const divideBy=m=>n=>n/m;export const divideInto=m=>n=>m/n;export const subtract=m=>n=>n-m;export const subtractFrom=m=>n=>m-n;export const modulo=m=>n=>n%m;export const moduloWholePart=m=>n=>{const div=n/m;const flo=Math.floor(div);return div<0?1+flo:flo;};export const toThe=e=>b=>Math.pow(b,e);// ------ exceptions // --- @todo consider adding tryCatchS: // const tryCatchS = (good) => (bad) => (f) => (v) => { // ... successVal = f (v) // } export const tryCatch=good=>bad=>f=>{// @todo simplify: try { return good (f ()) } let successVal;try{successVal=f();}catch(e){return bad(e);}return good(successVal);};/* Despite the name, `die` simply throws an exception, which can of course be caught. * (It shouldn't be too surprising to JS users that it doesn't really 'die', i.e. halt the runtime.) */export const exception=(...args)=>new Error(args.join(' '));export const raise=e=>{throw e;};export const die=composeRight(exception,raise);export const defaultTo=f=>x=>ok(x)?x:f();// ------ object stuff. export const prop=p=>o=>o[p];export const propOf=o=>p=>o[p];export const path=xs=>o=>{let j=o;for(const i of xs)if(!ok(j))return j;else j=j[i];return j;};export const pathOf=o=>xs=>path(xs)(o);// --- @todo update and assoc convert array input to an object unfortunately (due to the merge step). export const assoc=prop=>val=>o=>{const oo=mergeInM(o)(safeObject());oo[prop]=val;return oo;};export const assocM=prop=>val=>o=>(o[prop]=val,o);export const assocPath=xs=>x=>o=>assocPathM(xs)(x)(mergeInM(o)(safeObject()));export const assocPathM=xs=>x=>o=>{const reducer=(ptr,pat,el)=>{if(!ok(pat))return[ptr,el];const pp=ptr[pat];const ppp=isArray(pp)||isObject(pp)?pp:ptr[pat]=safeObject();return[ppp,el];};const[ptr,pat]=xs.reduce(([p,s],x)=>reducer(p,s,x),[o,null]);ptr[pat]=x;return o;};/* These always flatten the prototypes, like `assoc`. */export const updateM=prop=>f=>o=>(o[prop]=f(o[prop]),o);export const update=prop=>f=>o=>{const oo=merge(o)(safeObject());oo[prop]=f(o[prop]);return oo;};/* These are not separately unit tested because they route through `assocPath/M`, which are. */export const updatePathM=xs=>f=>o=>{const x=path(xs)(o);return assocPathM(xs)(f(x))(o);};export const updatePath=xs=>f=>o=>{const x=path(xs)(o);return assocPath(xs)(f(x))(o);};export const append=elem=>ary=>[...ary,elem];export const appendTo=ary=>elem=>[...ary,elem];export const appendToM=tgt=>src=>(tgt.push(src),tgt);export const appendM=src=>tgt=>(tgt.push(src),tgt);export const prependTo=ary=>elem=>[elem,...ary];export const prepend=elem=>ary=>[elem,...ary];export const prependM=src=>tgt=>(tgt.unshift(src),tgt);export const prependToM=tgt=>src=>(tgt.unshift(src),tgt);export const concatTo=tgt=>src=>tgt.concat(src);export const concat=src=>tgt=>tgt.concat(src);/* Note: these only work for arrays, not strings (strings are always immutable in JS) */export const concatToM=tgt=>src=>(tgt.push(...src),tgt);export const concatM=src=>tgt=>(tgt.push(...src),tgt);// --- @todo these seem to be much faster than Object.assign, check. export const mergeToM=tgt=>src=>{for(const i of Reflect.ownKeys(src)){tgt[i]=src[i];}return tgt;};export const mergeM=src=>tgt=>{for(const i of Reflect.ownKeys(src))tgt[i]=src[i];return tgt;};export const mergeTo=tgt=>src=>{const a=mergeToM(safeObject())(tgt);return mergeToM(a)(src);};export const merge=src=>tgt=>{const a=mergeToM(safeObject())(tgt);return mergeToM(a)(src);};/* The 'in' variants ignore all symbol keys. */export const mergeInToM=tgt=>src=>{for(const i in src)tgt[i]=src[i];return tgt;};export const mergeInM=src=>tgt=>mergeInToM(tgt)(src);export const mergeInTo=tgt=>src=>{const a=mergeInToM(safeObject())(tgt);return mergeInToM(a)(src);};export const mergeIn=src=>tgt=>mergeInTo(tgt)(src);const getMergeX=pluck=>mergerSym=>ifPredicate(ok)(pluck)(_=>die(sprintf('No merge function for symbol "%s"')))(merges()[mergerSym]);// --- throw on failure. const getMergeFunction=getMergeX(({f})=>f);const getMergeInfo=getMergeX(({to,mut,own})=>({to,mut,own}));export const mergeToMSym=Symbol('mergeToM');export const mergeToSym=Symbol('mergeTo');export const mergeMSym=Symbol('mergeM');export const mergeSym=Symbol('merge');export const mergeInToMSym=Symbol('mergeInToM');export const mergeInToSym=Symbol('mergeInTo');export const mergeInMSym=Symbol('mergeInM');export const mergeInSym=Symbol('mergeIn');// --- like R.mergeAll but also use prototype vals. // --- to and from not applicable, also not curried or meant to be used piped. export const mergeAllIn=xs=>xs.reduce((tgt,src)=>mergeInToM(tgt)(src),safeObject());const merges=_=>({[mergeToMSym]:{f:mergeToM,to:true,mut:true,own:true},[mergeToSym]:{f:mergeTo,to:true,mut:false,own:true},[mergeMSym]:{f:mergeM,to:false,mut:true,own:true},[mergeSym]:{f:merge,to:false,mut:false,own:true},[mergeInToMSym]:{f:mergeInToM,to:true,mut:true,own:false},[mergeInToSym]:{f:mergeInTo,to:true,mut:false,own:false},[mergeInMSym]:{f:mergeInM,to:false,mut:true,own:false},[mergeInSym]:{f:mergeIn,to:false,mut:false,own:false}});// --- tgt will be altered. // only `own` needs to be passed: direction and mutability have already been decided. // // in the M case tgt is just the tgt; // in the non-M case it has been prepared to be a new copy. // // `own` refers to both tgt & src -- not possible to mix and match. // --- a performance hit is acceptable here. const mergeXWith=collision=>own=>src=>tgt=>{const[_whenHas,_ifHas]=own?[whenHas,ifHas]:[whenHasIn,ifHasIn];const doKey=key=>_whenHas((v,o,k)=>_ifHas((v,o,k)=>tgt[key]=collision(src[key],tgt[key]))((o,k)=>tgt[key]=src[key])([tgt,key]))([src,key]);for(const key in src)doKey(key);// --- for non-own we ignore all symbol keys. if(own)for(const key of Object.getOwnPropertySymbols(src))doKey(key);return tgt;};export const mergeWith=collision=>mergerSym=>{// --- fail early instead of continuing with curry (throws) const merger=getMergeFunction(mergerSym);return a=>b=>{const{to,mut,own}=getMergeInfo(mergerSym);const[src,tgt]=to?[b,a]:[a,b];const tgtM=mut?tgt:to?merger(safeObject())(tgt):merger(tgt)(safeObject());return mergeXWith(collision)(own)(src)(tgtM);};};// --- like with 'with', mut and direction have already been arranged, and `tgt` will be mutated. // --- tests `p` for truthiness. const mergeXWhen=p=>own=>src=>tgt=>{const f=key=>{if(p(src[key],tgt[key]))tgt[key]=src[key];};if(own)for(const i of Reflect.ownKeys(src))f(i);else for(const i in src)f(i);return tgt;};// @todo mergeXWhen and mergeXWith should be one function => simpler export const mergeWhen=p=>mergerSym=>{// --- fail early instead of continuing with curry (throws) const merger=getMergeFunction(mergerSym);return a=>b=>{const{to,mut,own}=getMergeInfo(mergerSym);const[src,tgt]=to?[b,a]:[a,b];const tgtM=mut?tgt:to?merger(safeObject())(tgt):merger(tgt)(safeObject());return mergeXWhen(p)(own)(src)(tgtM);};};/* The following functions dispatch to the object's prototype method: `map`, `reduce`, * `reduceRight`, `find`, `filter`, `reject`. * * 'Capped' below refers to the `f` or `p` functions, not to the prototype function (e.g. `map` * itself). * * 'capped at 1' means the function will be called with exactly 1 argument. *//* `f` is capped at 1. */export const map=f=>xs=>xs.map(x=>f(x));export const each=f=>xs=>xs.forEach(x=>f(x));/* `f` is capped at 2. * dispatches to `xs.reduce` */export const reduce=f=>acc=>xs=>xs.reduce((acc,x)=>f(acc,x),acc);/* `f` is capped at 2. * * Does not dispatch to `xs.reduceRight`. Also, the order of `acc` and `f` within the reducer is reversed with respect to * `xs.reduceRight`, but is the familiar order from Haskell. * */export const reduceRight=f=>acc=>xs=>{let acco=acc;const l=xs.length;if(l===0)return acco;for(let i=l-1;i>=0;i-=1)acco=f(xs[i],acco);return acco;};/* Like `reduceRight`, but assumes `f` is curried. * * This allows you to write `f` in a more point-free way in certain cases, by omitting the accumulator. * * For example, a function which multiplies each element by 2 and reverses the list: * * const reverseDouble = reduceRight ( * (x, acc) => { * const clone = [... acc] * clone.push (x * 2) * return clone * }, * [], * ) * * can be written as * * const reverseDouble = reduceRight ( * (x, acc) => acc | append (x * 2), * [], * ) * * Then, using `reduceRightC`: * * const reverseDouble = reduceRightC ( * x => acc => acc | append (x * 2), * [], * ) * * And finally: * * const reverseDouble = reduceRightC ( * x => append (x * 2), * [], * ) * * And if you want to go further: * * const reverseDouble = reduceRightC ( * multiply (2) >> append, * [], * ) * * Note: does not work with addIndex/addCollection (will need separate 'C' versions of those). */export const reduceRightC=f=>accInit=>xs=>{let acc=accInit;const l=xs.length;if(l===0)return acc;for(let i=l-1;i>=0;i-=1)acc=f(xs[i])(acc);return acc;};/* `p` is capped at 1. * * Note that it's not possible to search for `undefined` in a list using this function. If you * really need this, consider `findIndex` or `contains`/`containsV`. */export const find=p=>xs=>xs.find(x=>p(x));export const findIndex=p=>xs=>{let i=0;for(const x of xs){if(p(x))return i;i++;}return undefined;};export const findWithIndex=p=>xs=>{let i=0;for(const x of xs){if(p(x))return[x,i];i++;}return[undefined,undefined];};/* `f` is capped at 1. */export const filter=f=>xs=>xs.filter(x=>f(x));export const reject=f=>xs=>xs.filter(x=>!f(x));export const containsV=v=>xs=>{for(const x of xs)if(x===v)return true;return false;};export const contains=p=>xs=>{for(const x of xs)if(p(x))return true;return false;};/* @experimental * * Not clear this is useful. abortVal should be a predicate instead of a val, and then, why not just * allow the caller to add a condition to `f`? */export const reduceAbort=f=>accInit=>abortVal=>xs=>{let acc=accInit;for(const x of xs){let g=f(acc,x);if(g===abortVal)return abortVal;acc=g;}return acc;};// --- dropping too many returns an empty list. export const drop=x=>xs=>xs.slice(x);// --- taking too many returns the entire list. export const take=x=>xs=>xs.slice(0,x);// --- returns obj. Ignores symbol keys of `o`. export const eachObj=f=>o=>{for(const k in o)if(hasOwnProperty.call(o,k))f(o[k],k);return o;};// --- returns obj export const eachObjIn=f=>o=>{for(const k in o)f(o[k],k);return o;};/* `addIndex` and `addCollection` work on lists and objects. */export const addIndex=orig=>f=>{let idx=-1;const g=(...args)=>f(...args,++idx);return orig(g);};export const addCollection=orig=>f=>coll=>{const g=(...args)=>f(...args,coll);return orig(g)(coll);};// --- successive forms can use the same function but just need to be recurried with different // arity. export const addIndex2=addIndex;/* const addIndexC = (orig) => (f) => { let idx = -1 // --- assumes args is non-empty (i.e., the function being mapped/reduced/etc. can't be called // without arguments. const g = (...args) => { const [x, ...ys] = args let o = f (x) for (const y in ys) o = o (y) return o (++idx) } return orig (g) } */export const addCollection2=orig=>f=>x=>coll=>{const g=(...args)=>f(...args,coll);return orig(g)(x)(coll);};/* Ignores symbol keys of `o`. */export const reduceObj=f=>acc=>o=>{let curAcc=acc;for(const k in o)if(hasOwnProperty.call(o,k))curAcc=f(curAcc,[k,o[k]]);return curAcc;};export const reduceObjIn=f=>acc=>o=>{let curAcc=acc;for(const k in o)curAcc=f(curAcc,[k,o[k]]);return curAcc;};/* --- @experimental: need more intuitive interface. * --- e.g. 3 | ampersandN ([inc, double, odd]) = [4, 6, true] * --- could alias `pam`: * const pam = ampersandN * 3 | pam ([inc, double, odd]) */export const ampersandN=fs=>x=>{const mapper=f=>f(x);return map(mapper)(fs);};/* @todo asteriskNN: fs => xs asterisk2N: f => g => xs asterisk2: f => g => a => b asteriskMapNN: fs => xs asteriskMap2N: f => g => xs asteriskMap2: f => g => a => b asteriskAppNN: xs => fs asteriskApp2N: a => b => fs asteriskApp2: a => b => f => g anvilNN asterisk = anvilNN const arrowSnd = f => timesV (2) >> asteriskN ([id, f]) */// --- @experimental // --- asterisk is like a dot product of vectors: it takes a list of functions and a list of values // and runs the functions corresponding to position. // --- @todo make more intuitive. export const asteriskN=fs=>xs=>{const ret=[];let i=-1;for(const f of fs){const x=xs[++i];ret.push(f(x));}return ret;};// --- @todo: asteriskN, for when given an array of functions and a corresponding array of callees. export const asterisk1=f=>a=>[f(a)];export const asterisk2=f=>g=>a=>b=>[f(a),g(b)];export const asterisk3=f=>g=>h=>a=>b=>c=>[f(a),g(b),h(c)];export const asterisk4=f=>g=>h=>i=>a=>b=>c=>d=>[f(a),g(b),h(c),i(d)];export const asterisk5=f=>g=>h=>i=>j=>a=>b=>c=>d=>e=>[f(a),g(b),h(c),i(d),j(e)];// --------- lets / let / let... export const letNV=xs=>f=>f.apply(null,xs);/* * lets = let* from racket * letN = let* + array * letS = let* + stick (S implies N) * lets1, lets2, etc.: wrapped by lets, but can be called directly too. * letNV = like letV, with array * letV = let with values instead of functions */// --- last arg must be a function. // --- 1 arg is possible but trivial. export const letV=(...xs)=>{const f=xs.pop();return letNV(xs)(f);};/* * For example, our `defaultTo` takes a function: * null | defaultTo (_ -> 'bad news') * For simple values, defaultToV can be more convenient: * null | defaultToV ('bad news') */// --- trivial form. export const lets1=f=>invoke(f);// --- @experimental lets2 - lets6 can be called directly as an optimisation (there is no pushing to // an array or spreading values) export const lets2=(f1,f2)=>{const n1=f1();return f2(n1);};export const lets3=(f1,f2,f3)=>{const n1=f1();const n2=f2(n1);return f3(n1,n2);};export const lets4=(f1,f2,f3,f4)=>{const n1=f1();const n2=f2(n1);const n3=f3(n1,n2);return f4(n1,n2,n3);};export const lets5=(f1,f2,f3,f4,f5)=>{const n1=f1();const n2=f2(n1);const n3=f3(n1,n2);const n4=f4(n1,n2,n3);return f5(n1,n2,n3,n4);};export const lets6=(f1,f2,f3,f4,f5,f6)=>{const n1=f1();const n2=f2(n1);const n3=f3(n1,n2);const n4=f4(n1,n2,n3);const n5=f5(n1,n2,n3,n4);return f6(n1,n2,n3,n4,n5);};export const letN=xs=>lets(...xs);export const lets=(...fs)=>{const acc=[];let last;for(const f of fs){const ret=f(...acc);acc.push(ret);last=ret;}return last;};export const letS=specAry=>tgt=>lets(_=>tgt,...specAry);// ------ call/provide export const callOn=o=>f=>f.call(o);export const callOn1=o=>val1=>f=>f.call(o,val1);export const callOn2=o=>val1=>val2=>f=>f.call(o,val1,val2);export const callOn3=o=>val1=>val2=>val3=>f=>f.call(o,val1,val2,val3);export const callOn4=o=>val1=>val2=>val3=>val4=>f=>f.call(o,val1,val2,val3,val4);export const callOn5=o=>val1=>val2=>val3=>val4=>val5=>f=>f.call(o,val1,val2,val3,val4,val5);export const callOnN=o=>vs=>f=>f.apply(o,vs);export const provideTo=f=>o=>f.call(o);export const provideTo1=f=>val=>o=>f.call(o,val);export const provideTo2=f=>val1=>val2=>o=>f.call(o,val1,val2);export const provideTo3=f=>val1=>val2=>val3=>o=>f.call(o,val1,val2,val3);export const provideTo4=f=>val1=>val2=>val3=>val4=>o=>f.call(o,val1,val2,val3,val4);export const provideTo5=f=>val1=>val2=>val3=>val4=>val5=>o=>f.call(o,val1,val2,val3,val4,val5);export const provideToN=f=>vs=>o=>f.apply(o,vs);export const invoke=f=>f();export const applyTo1=val1=>f=>f(val1);export const applyTo2=val1=>val2=>f=>f(val1,val2);export const applyTo3=val1=>val2=>val3=>f=>f(val1,val2,val3);export const applyTo4=val1=>val2=>val3=>val4=>f=>f(val1,val2,val3,val4);export const applyTo5=val1=>val2=>val3=>val4=>val5=>f=>f(val1,val2,val3,val4,val5);export const applyToN=vs=>f=>f.apply(null,vs);export const passTo=f=>val=>f(val);export const passToN=f=>vs=>f.apply(null,vs);export const spreadTo=passToN;// ------ join, split etc. export const join=dot1('join');export const split=dot1('split');export const flip=f=>a=>b=>f(b)(a);export const flip3=f=>a=>b=>c=>f(b)(a)(c);export const flip4=f=>a=>b=>c=>d=>f(b)(a)(c)(d);export const flip5=f=>a=>b=>c=>d=>e=>f(b)(a)(c)(d)(e);// ------ sprintf export const sprintf1=str=>a=>sprintf(str,a);export const sprintfN=str=>xs=>sprintf.apply(null,[str,...xs]);// ------ repeat, times export const repeatV=x=>n=>{const ret=[];for(let i=0;i<n;i++)ret.push(x);return ret;};export const repeatF=f=>n=>{const ret=[];for(let i=0;i<n;i++)ret.push(f(i));return ret;};export const repeatSide=f=>n=>{for(let i=0;i<n;i++)f(i);};export const timesV=n=>x=>repeatV(x)(n);export const timesF=n=>f=>repeatF(f)(n);export const timesSide=n=>f=>repeatSide(f)(n);// ------ replace / match export const ifReplace=yes=>no=>re=>replArg=>target=>{let success=0;const repl=typeof replArg==='function'?(...args)=>(++success,replArg(...args)):_=>(++success,replArg);const out=target.replace(re,repl);return success?yes(out,success):no(target);};// --- by should be negative to count down. export const rangeFromBy=by=>from=>to=>from<to?rangeFromByAsc(by)(from)(to):from>to?rangeFromByDesc(by)(from)(to):[];// --- no corresponding to version. export const rangeFromByAsc=by=>from=>to=>{const ret=[];for(let i=from;i<to;i+=by)ret.push(i);return ret;};// --- no corresponding to version. export const rangeFromByDesc=by=>from=>to=>{const ret=[];for(let i=from;i>to;i+=by)ret.push(i);return ret;};export const rangeToBy=by=>to=>from=>from<to?rangeFromByAsc(by)(from)(to):from>to?rangeFromByDesc(by)(from)(to):[];// --------- regex. // --- these deviate somewhat from the naming conventions: we're assuming you generally want to pipe // the target to the match functions. const removeSpaces=/*#__PURE__*/dot2('replace')(/\s+/g)('');// --- input: regex. export const xRegExp=re=>new RegExp(removeSpaces(re.source),re.flags);// @todo curry // --- beware, overwrites any flags that the re already had. export const xRegExpFlags=(re,flags)=>new RegExp(removeSpaces(re.source),flags);// @todo curry, check default flags. // --- input: string. export const xRegExpStr=(reStr,flags='')=>lets(_=>removeSpaces(reStr),_=>flags,(x,y)=>neu2(RegExp)(x)(y));// --- not every function (currently) has a matching 'replace' version. // @todo make xMatch incur only a compile-time cost. export const neu1=x=>val1=>new x(val1);export const neu2=x=>val1=>val2=>new x(val1,val2);export const neu3=x=>val1=>val2=>val3=>new x(val1,val2,val3);export const neu4=x=>val1=>val2=>val3=>val4=>new x(val1,val2,val3,val4);export const neu5=x=>val1=>val2=>val3=>val4=>val5=>new x(val1,val2,val3,val4,val5);export const neuN=x=>vs=>new x(...vs);export const match=re=>target=>re.exec(target);export const xMatchGlobal=re=>mapper=>target=>{let out=[];const reGlobal=xRegExpFlags(re,'g');let m;while(m=reGlobal.exec(target))appendToM(out)(mapper(...m));return out;};export const xMatch=re=>target=>xRegExp(re).exec(target);export const xMatchStr=reStr=>target=>xMatch(new RegExp(reStr))(target);export const xMatchStrFlags=reStr=>flags=>target=>xMatch(new RegExp(reStr,flags))(target);export const xReplace=re=>repl=>target=>target.replace(xRegExp(re),repl);export const xReplaceStr=reStr=>repl=>target=>target.replace(xRegExpStr(reStr),repl);export const xReplaceStrFlags=reStr=>flags=>repl=>target=>target.replace(xRegExpStr(reStr,flags),repl);export const ifXReplace=re=>repl=>yes=>no=>target=>ifReplace(yes)(no)(xRegExp(re))(repl)(target);export const ifXReplaceStr=reStr=>repl=>yes=>no=>target=>ifReplace(yes)(no)(xRegExpStr(reStr))(repl)(target);export const ifXReplaceStrFlags=reStr=>flags=>repl=>yes=>no=>target=>ifReplace(yes)(no)(xRegExpStr(reStr,flags))(repl)(target);/* The first arg can be either a function or an object. If it's a function, then it will be called * on each `create`. The object form avoids this extra call, but beware that if you use it then the * values should all be primitive types, or undefined or null. If they're reference types they will * be shared among all object instances and that's probably not what you want. You can initialise * the values during `init`. The optional object passed to `create` is assumed to only contain * string keys (symbol keys are ignored). */export const factoryProps=propsArg=>factory=>{const orig=(...args)=>factory.create(...args);// @todo don't clone factory? return{...factory,create(args){const props=typeof propsArg==='function'?propsArg():propsArg;const o=orig(props);const[src,tgt]=[args,o];for(const i in args)if(hasOwnProperty.call(src,i)&&ok(src[i]))tgt[i]=src[i];return tgt;}};};export const factoryInit=init=>proto=>({proto,create:props=>{const o=Object.create(proto);init(o,props);return o;}});// --- alters the prototype by merging in the mixins. // --- if a key exists in the proto and its value is not nil, then it is not overwritten. // --- the result is as if you had merged the proto into the mixin, keeping the proto's own // prototype chain intact, then called that result the proto; except that the proto is altered in // place of course because of the M. export const mixinPreM=mixin=>proto=>{const chooseTgtWhenOk=(src,tgt)=>ok(tgt)?tgt:src;const mergeInToChooseTgtWhenOkM=mergeWith(chooseTgtWhenOk)(mergeInToMSym);return mergeInToChooseTgtWhenOkM(proto)(mixin);};// --- alters the prototype by merging in the mixins. // --- if a key exists in the proto then it is overwritten, unless the corresponding value from the // mixin is nil. export const mixinM=mixin=>proto=>{const srcOk=(src,_)=>ok(src);const mergeInToWhenSrcOkM=mergeWhen(srcOk)(mergeInToMSym);return mergeInToWhenSrcOkM(proto)(mixin);};export const mixinPreNM=ms=>proto=>ms.reduce((protoAcc,mixin)=>mixinPreM(mixin)(protoAcc),proto);export const mixinNM=ms=>proto=>ms.reduce((protoAcc,mixin)=>mixinM(mixin)(protoAcc),proto);// ------ all/any. // --- no point in using an N marker, because non-n is only possible once you know the predicate. // ------ these all return truthy values instead of strict booleans; rationale: it's trivial to // convert to a boolean afterwards using for example `| Boolean` or `>> Boolean`, but not the other // way around. // --- returns the return value of the last function, if they all return truthy, otherwise `false`. export const againstAll=fs=>x=>{let y=false;for(const f of fs){y=f(x);if(!y)return false;}return y;};// --- returns the return value of the first function which returns truthy, otherwise `false`. export const againstAny=fs=>x=>{let y;for(const f of fs){y=f(x);if(y)return y;}return false;};// --- returns the return value of the last function if they are all truthy, otherwise `false`. export const allAgainst=f=>xs=>{let y=false;for(const x of xs){y=f(x);if(!y)return false;}return y;};// --- returns the return value of the first function which returns truthy, otherwise `false`. export const anyAgainst=f=>xs=>{let y;for(const x of xs){y=f(x);if(y)return y;}return false;};export const againstBoth=f=>g=>x=>f(x)&&g(x);export const againstEither=f=>g=>x=>f(x)||g(x);// @todo equiv: like R.equals // @todo pop, popFrom, popM, popFromM, also shift. /* * Usage: * const x = { some: 2, vals: 10, } * x | deconstruct ({ some, vals, }) => { * ... * } * x | deconstruct ({ some, vals, }, orig) => { * ... * } */export const deconstruct=f=>o=>f(o,o);/* * @experimental: need better name. * * Useful for when you want to deconstruct some arguments, but also keep a reference to the * original: * * Usage: * * const x = { some: 3, vals: 4, } * x | deconstruct2 (({ some, vals, }) => merge ({ * new: some + 2, * extra: vals + 3, * }) // => { some: 3, vals: 4, new: 5, extra: 7, } * * Or say you have a redux reducer function like: * * [CONSTANT]: (actionPayload) => (state) => newState * * where the newState depends on a value in the state: * * [INCREASE_COUNTER]: ({ data: increaseBy, }) => deconstruct2 ( * ({ counter, }) => state => state | merge ({ * counter: counter + increaseBy, * }), * ), * * or: * * [INCREASE_COUNTER]: ({ data: increaseBy, }) => deconstruct2 ( * ({ counter, }) => merge ({ * counter: counter + increaseBy, * }), * ), */export const deconstruct2=f=>o=>f(o)(o);/* * Deconstruct an array of values: * * Usage, e.g. in a React component method: * componentDidMount = this | deconstruct ( * ({ props, state, }) => [props, state] | deconstructN ( * ({ prop1, prop2, }, { state1, state2, }) => ... * ) * ) */export const deconstructN=f=>xs=>f(...xs);export const and=y=>x=>x&&y;export const or=y=>x=>x||y;/* export const andNot = not >> and * export const orNot = not >> or */export const andNot=y=>x=>x&&!y;export const orNot=y=>x=>x||!y;