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