Search code examples
javascriptmonadsreducecompositioncurrying

Reduce multiply functions


I didn't find a better title. In fact, the question is more about scope I guess. But I'll try to explain my problem. I've encountered here and there examples where a reduce method does nesting functions with the reference to previous accumulator and a current value that that was it a few steps ago. But it's the way how I see it although I'm not sure at all. That's why I came here to clarify my theory. I've tried to reread articles about reduce, composition and scope but all of them don't seem to be used in similar scenarios. Maybe I'm just bad at googling after all. Let's consider an example:

const composePromises = (...ms) => 
    ms.reduce((f, g) => x => g(x).then(f));

const g = n => Promise.resolve(n + 1);
const f = n => Promise.resolve(n * 2);
const z = n => Promise.resolve(n * 1.2);

const h = composePromises(z, f, g);

h(20);

So the question is: does x => g(x).then(f) go in then with fixed f: n => Promise.resolve(n * 1.2) and g: n => Promise.resolve(n * 2). Then after invocation h(20) when g = n => Promise.resolve(n + 1) got resolved and it's then's turn to produce, it resolves x => g(x).then(f) with fixed f and g to the functions I've mentioned before?

I've tried to describe as mush as I could and I hope you got my point. I just wanna understand how this kind of applications work. I spent a while to comprehend how references changes to f and g in a correct way. And that's the only one explanation I came up with.


Solution

  • Here is a simplified substitution evaluation. I focus only on the lambda (f, g) => x => g(x).then(f) and omit the reduce-machinery. I also changed some names (acc means accumulator but is still a function):

    const composePromises = (...ms) => 
      ms.reduce((acc, f) => x => f(x).then(acc));
    
    const myg = n => Promise.resolve(n + 1);
    const myf = n => Promise.resolve(n * 2);
    const myz = n => Promise.resolve(n * 1.2);
    
    // first reduction step:
    (acc, f) => x => f(x).then(acc); // apply acc with myz and f with myf
    // intermediate result:
    const acc_ = x => (n => Promise.resolve(n * 2)) (x).then(n => Promise.resolve(n * 1.2));
    
    // second reduction step:
    (acc, f) => x => f(x).then(acc); // apply acc with acc_ and f with myg
    // final result:
    const foo =
      x => (n => Promise.resolve(n + 1)) (x)
      .then(x =>
        (n => Promise.resolve(n * 2)) (x)
        .then(n => Promise.resolve(n * 1.2)));
    

    You can run the final result in the console and it will yield the expected result. I hope that helps.