Function composition adalah teknik menggabungkan beberapa function menjadi satu function baru yang menjalankan semuanya secara berurutan. Output function pertama menjadi input function berikutnya, dan seterusnya. Pada chapter ini kita akan memahami dua operator komposisi umum — compose (kanan-ke-kiri) dan andThen (kiri-ke-kanan) — serta melihat bagaimana pipeline transformasi dapat dibangun tanpa menulis nested call berlapis. Kita juga akan membahas dasar currying — teknik mengubah function banyak parameter menjadi rangkaian function satu parameter.
Apa Itu Function Composition #
Komposisi function f dan g menghasilkan function baru h sehingga h(x) = f(g(x)). Output g menjadi input f. Kotlin tidak memiliki operator bawaan untuk komposisi, tetapi kita dapat menulis sendiri dengan higher-order function.
// compose: f o g = (f compose g)(x) = f(g(x))
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C =
{ x -> f(g(x)) }
// andThen: g andThen f = (g andThen f)(x) = f(g(x))
infix fun <A, B, C> ((A) -> B).andThen(next: (B) -> C): (A) -> C =
{ x -> next(this(x)) }compose membaca dari kanan ke kiri (fungsional matematis), sedangkan andThen membaca dari kiri ke kanan (lebih natural untuk urutan eksekusi).
Contoh Function Composition #
Mari kita lihat komposisi function dalam beberapa skenario — numerik dan string processing.
// function_composition.kt
// Function composition: menggabungkan beberapa function menjadi pipeline.
// compose: (f o g)(x) = f(g(x))
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
return { x -> f(g(x)) }
}
// infix andThen: g andThen f = compose(f, g)
infix fun <A, B, C> ((A) -> B).andThen(next: (B) -> C): (A) -> C {
return { x -> next(this(x)) }
}
// Currying dasar: function dua parameter -> function bertingkat.
fun tambah(a: Int): (Int) -> Int = { b -> a + b }
fun main() {
val kalikan2: (Int) -> Int = { it * 2 }
val tambah3: (Int) -> Int = { it + 3 }
// compose: kalikan2 setelah tambah3 → (x + 3) * 2
val proses1 = compose(kalikan2, tambah3)
println("compose (x+3)*2 untuk 5: ${proses1(5)}")
// andThen: tambah3 andThen kalikan2 → (x + 3) * 2
val proses2 = tambah3 andThen kalikan2
println("andThen (x+3)*2 untuk 5: ${proses2(5)}")
// Pipeline panjang: tambah3 andThen kalikan2 andThen tambah3
val pipeline = tambah3 andThen kalikan2 andThen tambah3
// (5 + 3) * 2 + 3 = 19
println("Pipeline (x+3)*2+3 untuk 5: ${pipeline(5)}")
// Currying.
val tambah10 = tambah(10)
println("tambah10(7) = ${tambah10(7)}")
println("tambah(10)(7) = ${tambah(10)(7)}")
// Komposisi string processing.
val trim: (String) -> String = String::trim
val upper: (String) -> String = String::uppercase
val eksclam: (String) -> String = { it + "!" }
val prosesString = trim andThen upper andThen eksclam
val teksAsli = " halo kotlin "
println("Hasil pipeline: ${prosesString(teksAsli)}")
}Output yang Dihasilkan:
compose (x+3)*2 untuk 5: 16
andThen (x+3)*2 untuk 5: 16
Pipeline (x+3)*2+3 untuk 5: 19
tambah10(7) = 17
tambah(10)(7) = 17
Hasil pipeline: HALO KOTLIN!Mari kita telaah baris-baris kode di atas:
- Baris 6-8 (
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C): function genericcompose. Menerima dua functionfdang, mengembalikan function baru(A) -> C. Hasil pemanggilancompose(f, g)(x)sama denganf(g(x)). - Baris 11-13 (
infix fun <A, B, C> ((A) -> B).andThen(...)): extension function infix pada function type. OperatorandThenmembaca dari kiri ke kanan:(g andThen f)(x) = f(g(x)). Sama semantik dengancompose(f, g), hanya urutan argumen yang berbeda. - Baris 16 (
fun tambah(a: Int): (Int) -> Int = { b -> a + b }): dasar currying. Function dua parametertambah(a, b)dipecah menjadi rangkaian: pertama terimaadan kembalikan function, lalu function tersebut menerimab. - Baris 23 (
val proses1 = compose(kalikan2, tambah3)): komposisi(kalikan2 o tambah3). Untuk input5:tambah3(5) = 8, lalukalikan2(8) = 16. - Baris 27 (
val proses2 = tambah3 andThen kalikan2): komposisi yang sama, ditulis dari kiri ke kanan.tambah3dijalankan dulu, lalukalikan2. Hasil sama:16. - Baris 31 (
val pipeline = tambah3 andThen kalikan2 andThen tambah3): pipeline tiga tahap. Untuk input5:(5+3) * 2 + 3 = 19. InfixandThenmembuat pipeline terbaca seperti kalimat. - Baris 36-38 (
val tambah10 = tambah(10); tambah10(7)): pemanggilan currying dua tahap.tambah(10)mengembalikan function(Int) -> Int. Function tersebut lalu dipanggil dengan7, menghasilkan17. - Baris 39 (
tambah(10)(7)): currying dipanggil langsung dalam satu ekspresi.tambah(10)mengembalikan function, lalu(7)langsung memanggil function tersebut. - Baris 42-50 (
val trim = String::trim; ...; val prosesString = trim andThen upper andThen eksclam): pipeline string processing. Menggabungkan callable reference (String::trim,String::uppercase) dengan lambda ({ it + "!" }). Pipeline membersihkan spasi, mengkapitalisasi, lalu menambah tanda seru.
compose vs andThen #
| Aspek | compose(f, g) | g andThen f |
|---|---|---|
| Urutan eksekusi | g dulu, lalu f | g dulu, lalu f |
| Arah baca | Kanan ke kiri (matematis) | Kiri ke kanan (natural) |
| Hasil akhir | Sama | Sama |
| Idiomatic Kotlin | Jarang | Sering (infix lebih natural) |
Currying — Pengantar #
Currying adalah teknik mengubah function banyak parameter menjadi rangkaian function satu parameter. Function f(a, b, c) menjadi f(a)(b)(c). Manfaat utama currying adalah partial application — kita dapat menyediakan sebagian argumen dan mendapatkan function yang menunggu sisa argumen.
fun tambah(a: Int): (Int) -> Int = { b -> a + b }
val tambah10 = tambah(10) // function yang menunggu argumen kedua
val hasil = tambah10(7) // 17Currying tidak idiomatik di Kotlin seperti di Haskell, tetapi berguna untuk membangun function family yang dapat dikonfigurasi sebagian.
Referensi Lanjutan #
- Higher-order functions and lambdas — dasar komposisi function lewat higher-order function
- Inline functions
— komposisi function yang efisien via
inline
Penutup #
Function composition memungkinkan kita membangun pipeline transformasi yang ringkas dan dapat diuji secara modular. Setiap function kecil dapat diuji terpisah, lalu digabung menjadi pipeline kompleks via andThen. Currying menambah dimensi lain — partial application yang membuat function family dapat dikonfigurasi. Selanjutnya, kita akan masuk ke topik praktis yang menggabungkan banyak konsep lambdas: scope functions let, run, with, apply, dan also.