Fungsi biasa menerima data dan mengembalikan data. Higher-order function naik satu tingkat: menerima fungsi sebagai argumen, mengembalikan fungsi sebagai hasil, atau melakukan keduanya sekaligus. Konsep ini mungkin terdengar akademis, padahal Anda sudah lama memakainya — setiap kali menulis callback untuk array_map() atau usort(), kita sedang mengoper fungsi ke higher-order function milik PHP. Setelah pure function dan immutability, chapter ini membahas mesin yang membuat keduanya praktis di kode sehari-hari.
Fungsi sebagai Argumen #
Bentuk pertama higher-order function adalah fungsi yang menerima fungsi lain lewat parameter bertipe callable. Supaya mekanismenya terlihat telanjang, mari kita buat versi sederhananya sendiri:
<?php
// Higher-order function: menerima fungsi sebagai argumen.
function terapkanPadaSetiap(array $nilai, callable $transformasi): array
{
$hasil = [];
foreach ($nilai as $satu) {
$hasil[] = $transformasi($satu);
}
return $hasil;
}
$hargaNetto = [50000, 85000, 120000];
$hargaBruto = terapkanPadaSetiap(
$hargaNetto,
fn (int $harga): float => $harga * 1.11
);
echo implode(', ', $hargaBruto) . "\n";Output yang Dihasilkan:
55500, 94350, 133200Mari kita bedah perannya satu per satu:
- Parameter
$transformasibertipecallablemenandakan bahwa fungsi ini menerima fungsi lain sebagai masukan — itulah yang menjadikannya higher-order. - Baris
$hasil[] = $transformasi($satu);memanggil fungsi yang dikirim pemanggil untuk setiap elemen;terapkanPadaSetiap()sendiri tidak tahu transformasi apa yang terjadi. - Arrow function
fn (int $harga): float => $harga * 1.11adalah aturan PPN 11% yang dikirim sebagai argumen biasa, seperti mengoper angka atau string.
Tentu Anda sadar kita baru saja menulis ulang array_map() — dan memang itu intinya: array_map() adalah higher-order function yang sama, diproduksi pabrik. Dalam kode nyata pakailah array_map(); versi buatan ini hanya memperlihatkan isi mesinnya.
Callback di Berbagai Fungsi Bawaan #
PHP menyuntikkan gaya ini ke puluhan fungsi bawaan: array_map(), array_filter(), array_walk(), usort() beserta keluarganya, hingga preg_replace_callback(). Contoh klasiknya adalah pengurutan yang aturannya kita tentukan sendiri:
<?php
$peserta = [
['nama' => 'Budi', 'skor' => 88],
['nama' => 'Siti', 'skor' => 95],
['nama' => 'Andi', 'skor' => 71],
];
// Callback menentukan urutan; usort() yang mengatur array.
usort($peserta, fn (array $a, array $b): int => $b['skor'] <=> $a['skor']);
foreach ($peserta as $peringkat => $p) {
echo ($peringkat + 1) . ". {$p['nama']} ({$p['skor']})\n";
}Output yang Dihasilkan:
1. Siti (95)
2. Budi (88)
3. Andi (71)Fungsi usort() tidak tahu apa arti “lebih besar” untuk data kita; fungsi itu hanya berjanji memanggil callback dengan dua elemen dan menaati tandanya — negatif, nol, atau positif — untuk menyusun ulang array. Callback kita memakai spaceship operator $b['skor'] <=> $a['skor'] yang sengaja dibalik urutannya agar hasil akhir menurun dari skor tertinggi. Satu catatan jujur mengikuti chapter Immutability
: usort() termasuk kubu in-place, jadi $peserta memang berubah urutannya — namun callback-nya sendiri tetap murni, dan itulah bagian yang kita kendalikan.
Fungsi sebagai Hasil #
Bentuk kedua higher-order function justru mengembalikan fungsi. Manfaatnya: kita bisa membuat pabrik fungsi yang menghasilkan fungsi berperilaku mirip dengan konfigurasi berbeda:
<?php
// Higher-order function: MENGEMBALIKAN fungsi yang sudah dikonfigurasi.
function buatFormatterHarga(string $mataUang, string $simbol, int $desimal): callable
{
return fn (float $nilai): string
=> $simbol . number_format($nilai, $desimal, ',', '.') . ' ' . $mataUang;
}
$formatIdr = buatFormatterHarga('IDR', 'Rp', 0);
$formatUsd = buatFormatterHarga('USD', '$', 2);
echo $formatIdr(1500000) . "\n";
echo $formatUsd(49.99) . "\n";Output yang Dihasilkan:
Rp1.500.000 IDR
$49,99 USDSekali panggil, buatFormatterHarga('IDR', 'Rp', 0) menghasilkan fungsi siap pakai yang selalu memformat angka menjadi gaya rupiah; panggilan dengan argumen berbeda menghasilkan fungsi saudaranya bergaya dolar. Perhatikan pula hasilnya: dua fungsi lahir dari satu definisi, dan $formatIdr maupun $formatUsd adalah nilai biasa yang bisa disimpan di variabel, dimasukkan ke array, atau dioper ke fungsi lain. Mekanisme yang membuat fungsi hasil “mengingat” konfigurasinya adalah closure — kita bedah tuntas pada chapter berikutnya.
Partial Application #
Partial application adalah teknik menyediakan sebagian argumen sekarang, lalu menyisanya belakangan. PHP tidak punya sintaks bawaan untuk ini, tetapi higher-order function menutup celah tersebut:
<?php
function hargaDiskon(float $harga, float $persen): float
{
return $harga * (100 - $persen) / 100;
}
// Partial application: membekukan argumen kedua di dalam closure.
function buatDiskon(float $persen): callable
{
return fn (float $harga): float => hargaDiskon($harga, $persen);
}
$diskonMember = buatDiskon(10.0);
$diskonFlashSale = buatDiskon(25.0);
echo $diskonMember(200000) . "\n";
echo $diskonFlashSale(200000) . "\n";Output yang Dihasilkan:
180000
150000Fungsi buatDiskon() menerima persentase lalu mengembalikan fungsi satu-argumen yang “sudah tahu” persentasenya. Dari satu fungsi dua-argumen, kita memperoleh $diskonMember dan $diskonFlashSale — masing-masing siap diteruskan ke array_map() tanpa argumen tambahan. Pola ini sering dipakai untuk menyiapkan strategi berbeda dari mesin yang sama, dan menjadi bahan mentah penting saat kita membahas function composition nanti.
Combinator Secara Singkat #
Istilah combinator merujuk pada fungsi yang bekerja murni dengan mengombinasikan fungsi lain — tidak membaca keadaan luar, tidak menimbulkan efek. Dua contoh paling kecilnya:
<?php
// Combinator paling sederhana: identity — keluaran sama dengan masukan.
$identity = fn (mixed $nilai): mixed => $nilai;
// Combinator konstan: selalu mengabaikan masukan dan mengembalikan nilai tetap.
$selaluNol = fn (): int => 0;
var_dump($identity('PHP'));
var_dump($identity([1, 2, 3]));
var_dump($selaluNol());Output yang Dihasilkan:
string(3) "PHP"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
int(0)Combinator identity tampak tidak berguna, tetapi justru sering dipakai sebagai callback bawaan ketika sebuah API menuntut fungsi padahal kita tidak ingin mengubah apa pun. Combinator sungguhan berikutnya — yang merangkai dua fungsi menjadi satu — bernama compose, dan perannya cukup besar sehingga kami menyimpannya untuk Chapter Function Composition
.
Ringkasan #
Higher-order function memperlakukan fungsi sebagai warga kelas satu: masuk sebagai argumen callable, keluar sebagai nilai kembalian. Fungsi bawaan PHP memakai pola ini di mana-mana, usort() mencontohkan sisi argumennya, pabrik formatter mencontohkan sisi hasilnya, dan partial application menjembatani keduanya. Seluruh contoh pengembalian fungsi tadi bekerja karena satu mekanisme: closure yang menangkap lingkungan pembuatannya — itulah topik chapter berikutnya.