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 ujang= greetCowok('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(greet, title, name); {

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(this, args);

} else {

return function(...args2) {

return curried.apply(this, args.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(date, type, message) {

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(this, args);

} 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(this, args.concat(args2));

/* recursive call */

}

}

}

}

function logging(date, type, message) {

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