freddi301
8/30/2017 - 8:10 AM

JavaScript + Flow Named Curried Functions

JavaScript + Flow Named Curried Functions

/* @flow */

const createPerson = ({}: { name: string, birth: Date, pets: number }): [] => []

const a = {};
//createPerson(a);

const b = { ...a, name: 'fred' };
//createPerson(b);

const c = { ...b, birth: new Date };
//createPerson(c);

const d = { ...c, pets: 0 };
createPerson(d);

function curryByName1(fun, accumulatedArgs) { 
  return Object.assign(
  	partialArgs => curryByName1(fun, { ...accumulatedArgs, ...partialArgs }),
    { accumulatedArgs, fun }
  );
}

function curryByName2(fun, accumulatedArgs) { 
  return Object.assign(
  	partialArgs => curryByName2(fun, { ...accumulatedArgs, ...partialArgs }),
    { accumulatedArgs, fun }
  );
}

function curryByName3(fun, accumulatedArgs) { 
  return Object.assign(
  	partialArgs => curryByName3(fun, { ...accumulatedArgs, ...partialArgs }),
    { accumulatedArgs, fun }
  );
}

const x = curryByName1(createPerson, {})({ name: 'fred' })({ birth: new Date }).accumulatedArgs;
const y = curryByName2(createPerson, {})({ pets: 0 })({ birth: new Date }).accumulatedArgs;
const z = curryByName3(createPerson, {})({ pets: 0 })({ birth: new Date })({ name: 'fred' }).accumulatedArgs;

type CurriedByName<A: Object, O, AA: Object> = {
  <P: Object>(partialArgs: P): CurriedByName<A, O, AA & P>,
  accumulatedArgs: AA,
  fun: (a: A) => O
};
  
function curryByName<A: Object, O>(fun: (a: A) => O): CurriedByName<A, O, {}> {
  return (function curryByName_(fun, accumulatedArgs) { 
    return Object.assign(
      partialArgs => curryByName_(fun, { ...accumulatedArgs, ...partialArgs }),
      { accumulatedArgs, fun, run: execCurriedByName }
    );
  })(fun, {});
}

const createPersonCurriedByName = curryByName(createPerson);
const m = createPersonCurriedByName({ name: 'fred' })({ birth: new Date });
//m.fun(m.accumulatedArgs);
const n = createPersonCurriedByName({ pets: 0 })({ birth: new Date });
//n.fun(n.accumulatedArgs);
const o = createPersonCurriedByName({ name: 'fred' })({ birth: new Date })({ pets: 0 });
o.fun(o.accumulatedArgs);
    
function execCurriedByName<A: Object, O>(curried: CurriedByName<A, O, A>): O { return curried.fun(curried.accumulatedArgs); }

//execCurriedByName(m);
//execCurriedByName(n);
const r = execCurriedByName(o);

// example

const run = execCurriedByName;
const curry = curryByName;
function pipe2<A,B,C>(f: (a: A) => B, g: (a: B) => C): (a: A) => C { return a => g(f(a)); }

type Color = 'red' | 'black';
class Car { wheels: number; color: Color; made: Date; }

const buildCarFrame = ({ wheels, color, made = new Date }: { wheels: number, color: Color, made?: Date }) => Object.assign(new Car, { wheels, color, made });
const buildCar = curry(buildCarFrame);
    
const roboticArm3WheelsRed = vehicle => vehicle({ wheels: 3, color: 'red' });
const roboticArm2Wheels = vehicle => vehicle({ wheels: 2 });
const roboticArmColor = color => vehicle => vehicle({ color });
const roboticArmVeteran = vehicle => vehicle({ made: new Date('1970') });

const pipeline1 = pipe2(roboticArmVeteran, roboticArm3WheelsRed);
const pipeline2 = pipe2(roboticArm2Wheels, roboticArmColor('green'));
const pipeline3 = vehicle => vehicle({ wheels: 6 });

const car1 = run(pipeline1(buildCar));
const car2 = run(pipeline2(buildCar));
//const car3 = run(roboticArmColor('re')(buildCar));
//const car4 = run(buildCar);
const car5unfinished = pipeline3(buildCar);
car5unfinished.accumulatedArgs;
run(car5unfinished) // SHOULD BE ERROR