Currying merupakan metoda yang populer kalangan JavaScript developer untuk membuat function dimana argument-argument dari function tersebut di translate menjadi stand-alone function.

function(a, b) --> function(a)(b)
Di Javascript kita sah-sah saja membuat fungsi begini. Bahkan jadi hal yang umum
function add() {

  return function(a) {

    return function(b) {

      return a + b;

    }

  }

}
let test = add();

let result = test(2)(3); // a + b -> 2 + 3

console.log(resul); // 5
function diatas itu sama aja seperti function add(a, b) { return a + b } 
cuma kita buat "gaya" dikit biar lebih "keriting", agar membentuk format 
function(a)(b)

Contoh lain: kita ingin membuat fungsi untuk greeting seseorang dengan format: {greet}, {title}, {name}
function greeting(greet) {

  return function(title) {

    return function(name) {

      return greet + title + name;

    }

  }

}

let greetBudi = greetBudi('Haloo')(' Mr. ')('Budi');

console.log(greeting);  /* Haloo Mr. Budi */

/* bisa juga kita pakai seperti ini */

let greet = greeting('Dear, '); 

let budi = greet('Mr.')('Budi'); /* Dear, Mr.Budi */

let agus = greet('Mr.')('Agus'); /* Dear, Mr.Agus */

let nina = greet('Mrs.')('Nina'); /* Dear, Mrs.Nina */
Bisa juga di pakai begini...lebih enak untuk Pemisahan objek
let greetCowok = greeting('Hello, ')('Mr. '); 

let dedi = greetCowok('Dedi'); /* Hello, Mr.Dedi */

let ujanggreetCowok('Ujang'); /* Hello, Mr.Ujang */

let greetCewek = greeting('Hello, ')('Mrs. '); 

let siska = greetCewek('Siska'); /* Hello, Mrs.Siska */

let lili = greetCewek('Lili'); /* Hello, Mrs.Lili */
Gimana menarik kan? cara diatas lebih flexible dibanding kita pakei function biasa
function greeting_biasa(greettitlename); {

  return greet + title + name;

}

greeting_biasa('Hello ''Mr.''Agus'); /* Hello Mr.Agus */

greeting_biasa('Hello ''Mr.''Budi'); /* Hello Mr.Budi */

greeting_biasa('Hello ''Mr.''Dedi'); /* Hello Mr.Dedi */

kita perhatikan diatas, kita mesti berulang-ulang panggil greeting_biasa
ditambah paramater nya berulang-ulang juga. jadi kurang flexible dan oleh karena itu currying menjadi opsi yang harus kita coba.


Membuat helper function jadi bener-bener Curry

Curry yang advance biasanya bisa di kawinkan dengan Partial Application. 

1. normal call format  --> function(a, b, c);
2. curry-partial call format --> function(a)(b, c)
3. fully curry call format --> function(a)(b)(c)

Dan helper curry yang baik itu harus harus bisa memenuhi ke-3 format diatas. Berikut sample code nya
function curry(fn) {

  return function curried(...args)  { /* pakei spread operator */

    if (args.length >= fn.length) {

      return fn.apply(thisargs);

    } else {

      return function(...args2) {

        return curried.apply(thisargs.concat(args2)); /* recursive call */

      }

    }

  }

}
Dengan helper baru diatas curry(fn), baru kita bisa tuh pake ke-3 format tadi

Real-world Example. Misal kita ingin membuat fungsi untuk Logging system
function logging(datetypemessage) {

    return date + ':' + '['+type+']' + ' -> ' + message;

}

let
 log = curry(logging);


/* normal call */

log('today''DEBUG''Some debug message'); 

/* partial curry call */

log('today')('DEBUG''Some debug message'); 

/* full curry call */

log('today')('DEBUG')('some debug message'); 

let todayLog = log('today');

todayLog('DEBUG''some debug message');

todayLog('DEBUG')('some debug message');

let yesterdayLog = log('yesterday');

yesterdayLog('DEBUG''some debug message');

yesterdayLog('DEBUG')('some debug message');

let todayDebugLog = log('today''DEBUG'); /* bisa juga log('today')('DEBUG') */

todayDebugLog('Some debug message');

Memahami Algoritma-nya

Silahkan jalankan kode dibawah ini di browser atau di jsbin.com
function curry(fn) {

  return function curried(...args)  {

    console.log('args1: ' + args);

    console.log("length curry vs length original: " + args.length + ':' +fn.length)

    /**

     * Jika jumlah argument function yang mau di curry >= original function

     * langsung aja panggil/apply original function nya

     */

    if (args.length >= fn.length) { /* step #1 */

      console.log('masuk step #1');     

      return fn.apply(thisargs);

    } else {

      /**

       * Jika lebih sedikit, kita proses secara recursif

       * untuk meng-gabung/merge semua argument

       * sampai jumlahnya sama dengan original functionnya

       * dan akhirnya akan kembali masuk ke step #1

       */

      console.log('masuk step #2..');

      return function(...args2) { /* step #2 */

          console.log('args2: ' + args2)

          console.log('final argumen step args1+args2: ' + args.concat(args2));

          /* gabung argument menjadi [ar1, ar2, ...arn] */

          /* setelah argument di-merge, kita kembalikan/pass lagi ke function curried() diatas */

          return curried.apply(thisargs.concat(args2)); 

        /* recursive call */

      }

    }

  }

}

function logging(datetypemessage) {

return date + ':' + '['+type+']' + ' -> ' + message;

}

let normalLog = curry(logging); // kirim original function

let res = normalLog('today''debug''message 1');

console.log(res);

console.log('-------------------end normal call-------------------------');

let res2 = normalLog('today')('debug''message 1');

console.log(res2);

console.log('---------------------end partial call----------------------');

let res3 = normalLog('today')('debug')('message 1');

console.log(res3);

console.log('--------------------end of full-----------------------------');

Silahkan lihat outputnya di Console browser