Страницы

четверг, 4 августа 2016 г.

JavaScript curry

Случилось мне недавно каррировать функцию. Наверное проще было бы заимствовать уже готовый вариант у специально предназначенных для подобных практик инструментов - Lodash или Rambda, но мне этот вопрос показался тривиальным и вряд ли заслуживающим более 30 секунд времени на реализацию. Высказывание уважаемого Douglas Crockford отражает суть: "The making of good software takes time. If you try to make it take less time, it will take more time.".


Для примера возьмем функцию ...
const fn = (one, two, three) => ({ one, two, three });
... и попытаемся ее каррировать.

Моя 30-секундная реализация выглядела примерно так:
const curry = (fn) => {   
  const length = fn.length; 
  const arr = [];
  const f = (...args) => {    
    arr.push(...args);    
    return arr.length < length ? f : fn(...arr);
  };
  return f;
};

И на первый взгляд все нормально ...
... а на второй не очень ...

В результате второго подхода получился следующий код:
const curry = (fn) => {
  const f = (fn, length, arr) =>
    (...args) =>
      args.length < length ? f(fn, length - args.length, arr.concat(args)) : fn(...arr.concat(args));
  return f(fn, fn.length, []);
};

Может и не эталон, но решает вопрос:

Если убрать все новомодные свистелки, то этот код может выглядеть следующим образом:
var curry = function(fn) {
  return (function f(fn, length, arr) {
    return function() {
      var args = Array.prototype.slice.call(arguments);
      return args.length < length ? 
        f(fn, length - args.length, arr.concat(args)) : 
        fn.apply(null, arr.concat(args));
    };
  })(fn, fn.length, []);
};
 
Хозяйкам на заметку.