Higher-order function adalah function yang menerima function lain sebagai parameter atau mengembalikan function sebagai hasil. Konsep ini adalah inti pemrograman fungsional di Kotlin dan menjadi dasar banyak API standard library seperti map, filter, forEach, dan fold. Pada chapter ini kita akan melihat tiga bentuk utama higher-order function: yang menerima function sebagai parameter, yang mengembalikan function, dan yang mengkombinasikan keduanya. Kita juga akan menulis function customFilter yang mendemonstrasikan pola reusable yang dapat dipakai di banyak konteks.

Apa Itu Higher-Order Function #

Function di Kotlin adalah first-class citizen — dapat disimpan dalam variabel, diteruskan sebagai argumen, dan dikembalikan sebagai hasil. Higher-order function adalah function yang memanfaatkan kemampuan ini. Tiga bentuk paling umum:

  1. Menerima function sebagai parameter — misalnya map, filter.
  2. Mengembalikan function — misalnya factory function yang membuat transformer.
  3. Kombinasi keduanya — misalnya function composition.
kotlin Signature higher-order function
fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T>

Pada signature di atas, parameter predicate bertipe function type (T) -> Boolean. Pemanggil memberikan lambda yang menentukan kriteria filter.

Contoh Higher-Order Function #

Mari kita lihat ketiga bentuk higher-order function dalam satu file yang dapat dijalankan.

kotlin
// higher_order_functions.kt
// Higher-order function: menerima atau mengembalikan function.

// 1. Function yang menerima function sebagai parameter.
fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
    val hasil = mutableListOf<T>()
    for (e in this) {
        if (predicate(e)) hasil.add(e)
    }
    return hasil
}

// 2. Function yang mengembalikan function.
fun buatPenambah(basis: Int): (Int) -> Int {
    return { angka -> angka + basis }
}

// 3. Function yang menerima dua function sebagai parameter.
fun <T, U, R> kompose(
    transform1: (T) -> U,
    transform2: (U) -> R
): (T) -> R {
    return { nilai -> transform2(transform1(nilai)) }
}

fun main() {
    val daftar = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    // Memakai customFilter dengan lambda predicate.
    val genap = daftar.customFilter { it % 2 == 0 }
    println("Genap: $genap")

    val besar = daftar.customFilter { it > 5 }
    println("Lebih dari 5: $besar")

    // Memakai function yang mengembalikan function.
    val tambahSepuluh = buatPenambah(10)
    println("5 + 10 = ${tambahSepuluh(5)}")
    println("20 + 10 = ${tambahSepuluh(20)}")

    // Kombinasi dua function.
    val kaliDua: (Int) -> Int = { it * 2 }
    val tambahSatu: (Int) -> Int = { it + 1 }
    val prosesGabungan = kompose(kaliDua, tambahSatu)
    println("(3 * 2) + 1 = ${prosesGabungan(3)}")
}

Output yang Dihasilkan:

text
Genap: [2, 4, 6, 8, 10]
Lebih dari 5: [6, 7, 8, 9, 10]
5 + 10 = 15
20 + 10 = 30
(3 * 2) + 1 = 7

Mari kita telaah baris-baris kode di atas:

  1. Baris 5 (fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T>): extension function pada List<T> dengan parameter predicate bertipe function type generic (T) -> Boolean. Function mengembalikan List<T> baru berisi elemen yang lolos predikat.
  2. Baris 6-11: implementasi iterasi manual. Lambda predicate dipanggil untuk setiap elemen e; bila mengembalikan true, elemen dimasukkan ke hasil.
  3. Baris 14 (fun buatPenambah(basis: Int): (Int) -> Int): function yang mengembalikan function type (Int) -> Int. Function dalam ({ angka -> angka + basis }) menangkap basis dari lingkup luar — pola closure.
  4. Baris 21-26 (fun <T, U, R> kompose(...)): function generic yang menerima dua function sebagai parameter dan mengembalikan function baru. Hasil pemanggilan adalah komposisi transform2 ∘ transform1.
  5. Baris 34 (daftar.customFilter { it % 2 == 0 }): pemanggilan dengan trailing lambda. it merujuk ke setiap elemen daftar. Hasilnya list elemen genap.
  6. Baris 39 (val tambahSepuluh = buatPenambah(10)): variabel tambahSepuluh kini memegang function (Int) -> Int yang selalu menambah 10 ke inputnya. Bisa dipanggil berulang dengan argumen berbeda.
  7. Baris 45 (val prosesGabungan = kompose(kaliDua, tambahSatu)): function baru hasil komposisi tambahSatu ∘ kaliDua — pertama kalikan dua, lalu tambah satu.

Pola Reusable yang Umum #

Higher-order function membantu kita mengekstrak pola berulang menjadi function reusable. Beberapa pola yang sering muncul:

PolaSignature SingkatContoh di Stdlib
Filter(T) -> Booleanfilter, takeWhile, any, all
Map(T) -> Rmap, mapNotNull, flatMap
Fold/Accumulate(Acc, T) -> Accfold, reduce
Compose((T) -> U, (U) -> R) -> (T) -> R(custom)
Tap/Side effect(T) -> UnitforEach, onEach

Mengapa Higher-Order Function Penting #

Higher-order function mengubah cara kita berpikir tentang pengulangan dan transformasi data. Alih-alih menulis loop manual untuk setiap kasus, kita menulis satu function umum yang menerima strategi spesifik via lambda. Pola ini membuat kode lebih deklaratif — kita menyatakan apa yang ingin dicapai, bukan bagaimana mencapainya.

Referensi Lanjutan #

Penutup #

Higher-order function adalah pondasi yang membuat Kotlin terasa ekspresif untuk pemrograman fungsional. Dengan mampu menerima dan mengembalikan function, kita dapat membangun API reusable yang ringkas. Selanjutnya, kita akan membedah tipe function secara formal — mulai dari (Int, Int) -> Int hingga suspend function type dan receiver function type — pada chapter Function Types.