Функциональная композиция/последовательность Javascript

Я пытаюсь понять концепцию композиции и последовательности в Javascript с помощью упражнения:

Защ. композиция compose(f,g)(x) = f(g(x))

Защ. последовательность sequence(f,g)(x) = g(f(x)) для большего количества аргументов sequence(f,g)(...args) = g(f(...args))

const sequence2 = (f1, f2) => (...args) => f2( f1(...args) );
const sequence = (f1, ...fRest) => fRest.reduce(sequence2, f1);

const f1 = (a, b) => {
    console.log(`[f1] working on: ${a} and ${b}`);
    return a + b;
}

const f2 = a => `Result is ${a}`;

const sequenceResult = sequence(f1, f1, f2)(1, 2, 5);
console.log(sequenceResult);

Консоль показывает:

[f1] working on: 1 and 2
[f1] working on: 3 and undefined
Result is NaN

Похоже, что вторая функция в последовательности не может получить доступ к аргументам: я что-то упускаю или это неправильный подход к работе с параметрами? (Функция последовательности работает для функций без параметров).

Здесь JSFiddle


person zerologiko    schedule 20.04.2020    source источник
comment
В g(f(x)) g не может (и не должен!) получить доступ к x. Все нормально и ведет себя так, как ожидалось.   -  person Bergi    schedule 20.04.2020


Ответы (2)


Кажется, что вторая функция в последовательности не может получить доступ к аргументам

Да, это нормально и ожидаемо. По определению, которое вы дали,

sequence(f1, f1, f2)(1, 2, 5);

эквивалентно

f2(f1(f1(1, 2, 5)));

Конечно, ни f2, ни внешний f1 не могут получить доступ к аргументам, которые передаются внутреннему f1.

person Bergi    schedule 20.04.2020
comment
Спасибо, эквивалентность очень хорошо объясняет, почему внешним функциям доступно только возвращаемое значение. - person zerologiko; 20.04.2020

Функции возвращают только одно значение. Есть два способа дополнить функции несколькими возвращаемыми значениями:

  • вернуть массив как кортеж
  • вместо возврата вызовите продолжение

Вот забавная реализация для последнего, которая явно не предназначена для какого-либо производственного кода:

const pipek = g => f => x => y => k =>
  k(g(x) (y) (f));

const addk = x => y => k =>
  (console.log("apply addk to", x, y), k(x + y));

const main = pipek(addk)
  (addk)
    (2)
      (3)
        (k => k(4)); // we have to pass the 4th argument within the continuation

console.log(main(x => x)); // escape from the continuation

Обратите внимание, что все функции являются каррированными, и я использовал термин pipe, который является обычным термином для обратной композиции функций в JS.

person Iven Marquardt    schedule 20.04.2020
comment
Спасибо, возврат массива типа кортежа - это какое-то решение. - person zerologiko; 20.04.2020