Como implementar uma função de redução mais geral para permitir a saída antecipada?

reduce (akafoldL no FP) é a função iterativa de ordem superior mais geral em Javascript. Você pode implementar, por exemplo,map oufilter em termos dereduce. Eu usei um loop imperativo para ilustrar melhor o algoritmo:

const foldL = f => acc => xs => {
  for (let i = 0; i < xs.length; i++) {
    acc = f(acc)(xs[i]);
  }

  return acc;
};

const map = f => xs => {
  return foldL(acc => x => acc.concat([f(x)]))([])(xs);
}

let xs = [1, 2, 3, 4];
const inc = x => ++x;

map(inc)(xs); // [2, 3, 4, 5]

Mas você não pode derivarsome ouevery dereduce, porque ambos são capazes de retornar mais cedo.

Então, como pode ser uma função de redução parcial ainda mais generalizada? Até agora, eu vim com a seguinte implementação ingênua:

const foldLP = f => pred => acc => xs => {
  for (let i = 0, r; i < xs.length; i++) {
    r = pred(i, acc, xs[i]);

    if (r === true) { // normal iteration
      acc = f(acc)(xs[i]);
    } else if (r === false) { // early exit
      break;
    } /* else { // skip iteration
      continue;
    } */
  }

  return acc;
};

const takeN = n => (idx, acc, x) => idx < n;
const append = xs => ys => xs.concat(ys);

let xs = [1,2,3,4,5];
foldLP(append)(takeN(3))([])(xs); // [1,2,3]

Eu também posso implementarmap em termos defoldLP:

const always = x => y => x;
const map = f => xs => {
  return foldLP(acc => x => acc.concat([f(x)]))(always(true))([])(xs);
}

map(inc)(xs); // [2,3,4,5,6]

A desvantagem é óbvia: sempre que um mecanismo de saída antecipada não é necessário,always é chamado desnecessariamente. A função de transformação e saída precoce é composta estaticamente porfoldLP e não pode ser usado independentemente. Existe um combinador mais eficiente, que permita uma generalizaçãoArray.prototype.reduce?

Se você olhar para a pilha de chamadas, a declaração de retorno da função de reduçãoacc => x => acc.concat([f(x)]) teria que pular vários quadros de pilha. Esse tipo de manipulação de pilha me faz pensar em continuações. Talvez haja uma maneira eficiente de resolver esse problema no estilo de passagem de continuação, juntamente com uma função de chamada / cc adaptada - ou pelo menos com um gerador.

questionAnswers(2)

yourAnswerToTheQuestion