Kotlin

Functional Programming

Functional Programming (FP) adalah paradigma pemrograman yang menekankan penggunaan fungsi-fungsi murni, imutabilitas data, dan penghindaran perubahan status (state) secara langsung.

Author : Celvine Adi Putra | Date : November 25, 2025

Functional Programming

Functional Programming (FP) adalah paradigma pemrograman yang menekankan penggunaan fungsi-fungsi murni, imutabilitas data, dan penghindaran perubahan status (state) secara langsung. Dalam beberapa tahun terakhir, paradigma ini semakin populer karena kemampuannya menghasilkan kode yang lebih ringkas, mudah diuji, dan aman dari efek samping yang tidak diinginkan. Kotlin, sebagai bahasa pemrograman modern yang berjalan di atas Java Virtual Machine (JVM), mendukung paradigma ini secara penuh, bahkan mengintegrasikannya dengan paradigma lain seperti Object-Oriented Programming (OOP).

Pada materi ini, kita akan membahas konsep-konsep utama functional programming dalam Kotlin, mulai dari pengertian dasar, pure functions, lambda dan higher-order functions, penggunaan inline dan tailrec, extensions, sequences, hingga perbandingan paradigma fungsional dan imperatif.


1. Pengertian Functional Programming dalam Kotlin

1.1 Definisi dan Karakteristik

Functional Programming adalah paradigma pemrograman yang membangun program berdasarkan fungsi-fungsi murni, menghindari perubahan status, dan meminimalkan efek samping. Dalam FP, fungsi diperlakukan sebagai entitas kelas pertama (first-class citizens), artinya fungsi dapat disimpan dalam variabel, diteruskan sebagai parameter, dan dikembalikan sebagai hasil dari fungsi lain.

Karakteristik utama FP di Kotlin meliputi:

  • Immutability: Data tidak dapat diubah setelah dibuat. Penggunaan val dan koleksi immutable (List, Map, Set) sangat dianjurkan.
  • Pure Functions: Fungsi yang selalu menghasilkan output yang sama untuk input yang sama dan tidak memiliki efek samping.
  • First-Class & Higher-Order Functions: Fungsi dapat diperlakukan sebagai data, diteruskan, dan dikembalikan.
  • Function Composition: Fungsi dapat dikombinasikan untuk membentuk fungsi baru.
  • Lazy Evaluation: Eksekusi kode ditunda hingga benar-benar dibutuhkan, misalnya dengan sequences.
  • Declarative Style: Fokus pada “apa yang ingin dicapai” daripada “bagaimana melakukannya”.

Kotlin mengadopsi paradigma ini dengan menyediakan sintaks dan pustaka standar yang kaya akan fungsi-fungsi fungsional seperti map, filter, reduce, fold, dan lain-lain.

1.2 Contoh Sederhana

Example.kt
// Menggunakan fungsi map dan filter secara fungsional
val numbers = listOf(1, 2, 3, 4, 5)
val evenSquares = numbers.filter { it % 2 == 0 }.map { it * it }
println(evenSquares) // Output: [4, 16]

Pada contoh di atas, data tidak diubah secara langsung, melainkan menghasilkan koleksi baru berdasarkan transformasi yang dilakukan secara deklaratif.


2. Pure Function

2.1 Pengertian Pure Function

Pure function adalah fungsi yang memenuhi dua syarat utama:

  1. Deterministik: Selalu menghasilkan output yang sama untuk input yang sama.
  2. Tanpa Efek Samping: Tidak mengubah status di luar ruang lingkupnya, tidak melakukan I/O, tidak mengubah variabel global, dan tidak mengakses resource eksternal.

Pure function sangat penting dalam FP karena membuat kode lebih mudah diuji, diprediksi, dan aman untuk dijalankan secara paralel.

2.2 Contoh Pure dan Impure Function

example-pure-impure-function.kt
// Pure Function
fun add(a: Int, b: Int): Int = a + b

// Impure Function (memodifikasi variabel global)
var counter = 0
fun increment(): Int {
    counter += 1
    return counter
}

Pada contoh di atas, add adalah pure function karena tidak tergantung pada status eksternal dan tidak mengubah apapun di luar dirinya. Sebaliknya, increment adalah impure function karena mengubah variabel global counter.

2.3 Manfaat Pure Function

  • Mudah Diuji: Karena output hanya bergantung pada input, pengujian menjadi sederhana.
  • Mudah Ditebak: Tidak ada efek samping, sehingga mudah diprediksi.
  • Aman untuk Paralelisme: Tidak ada status bersama yang diubah, sehingga aman untuk dijalankan secara bersamaan.
  • Mendukung Refactoring: Kode lebih mudah diubah tanpa khawatir efek samping tersembunyi.

2.4 Praktik Terbaik Pure Function

  • Gunakan koleksi dan data immutable (val, List, Map).
  • Hindari akses atau modifikasi variabel global.
  • Pisahkan logika murni dari operasi I/O.
  • Buat semua dependensi eksplisit melalui parameter fungsi.

2.5 Contoh Kode Pure Function

pure-function.kt
// Fungsi pure untuk menghitung diskon
data class Product(val name: String, val price: Double)
data class Discount(val percentage: Double)

fun calculateDiscountedPrice(product: Product, discount: Discount): Double {
    return product.price * (1 - discount.percentage / 100)
}

Fungsi di atas hanya bergantung pada input dan tidak mengubah status eksternal.


3. Lambda Expressions di Kotlin

3.1 Pengertian Lambda

Lambda expression adalah fungsi anonim (tanpa nama) yang dapat didefinisikan secara inline. Lambda sangat berguna untuk operasi-operasi singkat, terutama sebagai argumen pada higher-order function.

Sintaks dasar lambda di Kotlin:

lambda.kt
val sum = { a: Int, b: Int -> a + b }
println(sum(2, 3)) // Output: 5

3.2 Lambda sebagai First-Class Citizen

Fungsi di Kotlin adalah first-class citizen, artinya dapat:

  • Disimpan dalam variabel
  • Diteruskan sebagai parameter
  • Dikembalikan dari fungsi lain

Contoh:

lamda-function.kt
val greet: (String) -> Unit = { name -> println("Hello, $name!") }
greet("Kotlin") // Output: Hello, Kotlin!

3.3 Lambda sebagai Parameter Fungsi

lamda-as-parameter-function.kt
fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

val result = operateOnNumbers(5, 3) { x, y -> x + y }
println(result) // Output: 8

3.4 Lambda dengan Satu Parameter (it)

Jika lambda hanya memiliki satu parameter, Kotlin menyediakan nama implisit it:

lamda-with-one-parameter-function.kt
val numbers = listOf(1, 2, 3, 4, 5)
val even = numbers.filter { it % 2 == 0 }
println(even) // Output: [2, 4]

3.5 Lambda sebagai Extension Function

Lambda juga dapat digunakan sebagai extension function:

lamda-as-extention-function.kt
val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
println("Hi".repeatFun(3)) // Output: HiHiHi

3.6 Cara Terbaik Menggunakan Lambda

  • Gunakan lambda untuk operasi singkat dan sederhana.
  • Hindari lambda yang terlalu kompleks atau dalam (nested) agar kode tetap mudah dibaca.
  • Manfaatkan trailing lambda untuk meningkatkan keterbacaan.

4. Higher-Order Functions (HOF)

4.1 Pengertian Higher-Order Function

Higher-order function adalah fungsi yang menerima fungsi lain sebagai parameter dan/atau mengembalikan fungsi sebagai hasilnya. HOF adalah pilar utama dalam FP karena memungkinkan abstraksi operasi dan komposisi logika.

Contoh sederhana:

hoc-function.kt
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}

fun sum(x: Int, y: Int) = x + y

fun main() {
    val result = calculate(2, 3, ::sum)
    println("The sum is: $result") // Output: The sum is: 5
}

4.2 Contoh Penggunaan HOF pada Koleksi

Kotlin menyediakan banyak HOF pada koleksi, seperti map, filter, reduce, fold:

hoc-function-example.kt
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
val even = numbers.filter { it % 2 == 0 }
val sum = numbers.reduce { acc, i -> acc + i }

4.3 HOF yang Mengembalikan Fungsi

hoc-return-fn-example.kt
fun createMultiplier(factor: Int): (Int) -> Int {
    return { number: Int -> number * factor }
}

val double = createMultiplier(2)
println(double(4)) // Output: 8

4.4 Komposisi Fungsi

HOF memungkinkan komposisi fungsi:

hoc-composition-function-example.kt
fun compose(f: (Int) -> Int, g: (Int) -> Int): (Int) -> Int {
    return { x: Int -> f(g(x)) }
}

fun square(x: Int) = x * x
fun increment(x: Int) = x + 1

val squareOfIncrement = compose(::square, ::increment)
println(squareOfIncrement(5)) // Output: 36

4.5 Cara Terbaik Menggunakan Lambda

  • Gunakan HOF untuk mengabstraksi pola operasi yang berulang.
  • Hindari penggunaan HOF yang terlalu dalam (deep nesting) agar kode tetap mudah dibaca.
  • Manfaatkan HOF pada koleksi untuk operasi data yang deklaratif.

5. Inline Functions dan Penggunaan inline

5.1 Pengertian Inline Function

Inline function adalah fungsi yang diberi modifier inline, sehingga saat dipanggil, isi fungsi dan lambda yang diteruskan akan disalin langsung ke tempat pemanggilan oleh compiler. Tujuannya adalah mengurangi overhead pembuatan objek fungsi dan meningkatkan performa, terutama pada HOF yang sering dipanggil dalam loop.

5.2 Contoh Inline Function

inline-function.kt
inline fun execute(block: () -> Unit) {
    println("Start")
    block()
    println("End")
}

fun main() {
    execute { println("Inside block") }
}
// Output:
// Start
// Inside block
// End

Tanpa inline, lambda block akan dibuat sebagai objek terpisah, sedangkan dengan inline, kode dalam block akan langsung disisipkan ke tempat pemanggilan.

5.3 Keuntungan dan Risiko Inline

Keuntungan:

  • Mengurangi overhead alokasi objek lambda.
  • Memungkinkan non-local return dari lambda.
  • Meningkatkan performa pada fungsi kecil yang sering dipanggil.

Risiko:

  • Code bloat jika digunakan pada fungsi besar.
  • Stack trace debugging bisa menjadi kurang jelas.
  • Tidak semua lambda bisa di-inline (gunakan noinline jika perlu).

5.4 Modifier Terkait: noinline dan crossinline

  • noinline: Menandai parameter lambda agar tidak di-inline.
  • crossinline: Mencegah penggunaan non-local return dalam lambda.

Contoh:

x.kt
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }

5.5 Praktik Terbaik Inline

  • Gunakan inline hanya pada fungsi kecil dan sering dipanggil.
  • Hindari inlining pada fungsi besar untuk mencegah code bloat.
  • Gunakan crossinline untuk mencegah return tak terduga dari lambda.

6. Tail Recursion dan tailrec

6.1 Pengertian Tail Recursion

Tail recursion adalah teknik rekursi di mana pemanggilan fungsi rekursif terjadi sebagai operasi terakhir dalam fungsi tersebut. Kotlin dapat mengoptimalkan tail recursion menjadi loop, sehingga menghindari stack overflow pada rekursi yang dalam.

6.2 Syarat Tail Recursion

  • Pemanggilan rekursif harus menjadi operasi terakhir dalam fungsi.
  • Fungsi harus diberi modifier tailrec.

6.3 Contoh Tail Recursion

Faktorial Biasa (tidak tail-recursive)

factorial-tail-recursive.kt
fun factorial(n: Int): Long {
    return if (n == 1) 1 else n * factorial(n - 1)
}

Faktorial dengan Tail Recursion

factorial-tail-recursion.kt
tailrec fun factorial(n: Int, acc: Long = 1): Long {
    return if (n == 1) acc else factorial(n - 1, acc * n)
}

Fibonacci dengan Tail Recursion

fibonacci-tail-recursion.kt
tailrec fun fibonacci(n: Int, a: Long = 0, b: Long = 1): Long {
    return if (n == 0) a else fibonacci(n - 1, b, a + b)
}

6.4 Manfaat Tail Recursion

  • Menghindari stack overflow pada rekursi dalam.
  • Performa setara dengan loop imperatif.
  • Kode tetap deklaratif dan mudah dibaca.

6.5 Praktik Terbaik Tail Recursion

  • Gunakan tail recursion untuk operasi rekursif yang dalam.
  • Pastikan pemanggilan rekursif adalah operasi terakhir.
  • Gunakan parameter akumulator untuk membawa hasil perhitungan.

7. Extensions (Extension Functions dan Properties)

7.1 Pengertian Extension Function

Extension function memungkinkan kita menambahkan fungsi baru ke kelas yang sudah ada tanpa mengubah kode sumber aslinya. Extension function sangat berguna untuk menambah utilitas pada kelas bawaan atau pihak ketiga.

Sintaks:

extension.kt
fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}

println("katak".isPalindrome()) // Output: true

7.2 Extension Properties

Selain fungsi, Kotlin juga mendukung extension properties:

extension-properties.kt
val String.lastChar: Char
    get() = this[this.length - 1]

println("Kotlin".lastChar) // Output: n

7.3 Extension pada Nullable Receiver

Extension dapat didefinisikan pada tipe nullable:

extension-nullable-receiver.kt
val Int?.half: Int
    get() = this?.div(2) ?: 0

val nilai: Int? = null
println(nilai.half) // Output: 0

7.4 Extension pada Interface dan Companion Object

Extension dapat diterapkan pada interface dan companion object:

extension-interface-companion-object.kt
interface User { val name: String }
fun User.greet() = "Hello, $name!"

class RegularUser(override val name: String) : User
val user = RegularUser("Alice")
println(user.greet()) // Output: Hello, Alice!

7.5 Praktik Terbaik Extension

  • Gunakan extension untuk menambah utilitas tanpa mengubah kode asli.
  • Hindari overuse agar kode tidak membingungkan.
  • Jangan gunakan extension untuk menambah state; hanya perilaku (fungsi).
  • Gunakan nama yang jelas dan tidak ambigu.

8. Sequences dan Lazy Evaluation

8.1 Pengertian Sequence

Sequence di Kotlin adalah koleksi yang dievaluasi secara lazy (malas), artinya operasi seperti map, filter, dan lain-lain tidak langsung dieksekusi, melainkan baru dijalankan saat dibutuhkan (misal saat toList() atau forEach()).

8.2 Perbedaan List vs Sequence

AspekList (Eager)Sequence (Lazy)
EvaluasiLangsung (eager)Ditunda (lazy)
IntermediateMembuat koleksi baruTidak membuat koleksi
PerformaKurang efisien pada operasi berantaiLebih efisien untuk operasi berantai dan data besar

8.3 Contoh Penggunaan Sequence

sequence.kt
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.asSequence()
    .map { it * 2 }
    .filter { it > 5 }
    .toList()
println(result) // Output: [6, 8, 10]

Pada contoh di atas, operasi map dan filter tidak langsung dieksekusi, melainkan baru dijalankan saat toList() dipanggil.

8.4 Manfaat Sequence

  • Efisiensi Memori: Tidak membuat intermediate collection.
  • Performa: Cocok untuk data besar atau operasi berantai panjang.
  • Mendukung Infinite Sequence: Dapat digunakan untuk data tak hingga dengan generateSequence.

8.5 Praktik Terbaik Sequence

  • Gunakan sequence untuk operasi berantai pada data besar.
  • Hindari sequence pada koleksi kecil (overhead lazy lebih besar dari manfaatnya).
  • Gunakan terminal operation (toList, forEach, dll) untuk mengeksekusi sequence.

9. Perbandingan Paradigma Fungsional vs Imperatif dalam Kotlin

9.1 Tabel Perbandingan

AspekFunctional Programming (FP)Imperative Programming (IP)
FokusApa yang ingin dicapai (what)Bagaimana melakukannya (how)
StateImmutability, tanpa perubahan stateMutable state, perubahan variabel
FungsiPure, first-class, higher-orderProsedural, side effect umum
Kontrol AlurDeklaratif, komposisi fungsiUrutan perintah eksplisit
ParalelismeMudah, aman dari race conditionRentan race condition
KodeRingkas, ekspresif, mudah diujiPanjang, detail, rawan bug
Contohmap, filter, reduceLoop for, while, assignment

9.2 Contoh Kode: FP vs Imperatif

Imperatif

imperatif.kt
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = mutableListOf<Int>()
for (n in numbers) {
    doubled.add(n * 2)
}
println(doubled) // Output: [2, 4, 6, 8, 10]

Fungsional

functional.kt
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
println(doubled) // Output: [2, 4, 6, 8, 10]

On this page

Functional Programming
1. Pengertian Functional Programming dalam Kotlin
1.1 Definisi dan Karakteristik
1.2 Contoh Sederhana
2. Pure Function
2.1 Pengertian Pure Function
2.2 Contoh Pure dan Impure Function
2.3 Manfaat Pure Function
2.4 Praktik Terbaik Pure Function
2.5 Contoh Kode Pure Function
3. Lambda Expressions di Kotlin
3.1 Pengertian Lambda
3.2 Lambda sebagai First-Class Citizen
3.3 Lambda sebagai Parameter Fungsi
3.4 Lambda dengan Satu Parameter (it)
3.5 Lambda sebagai Extension Function
3.6 Cara Terbaik Menggunakan Lambda
4. Higher-Order Functions (HOF)
4.1 Pengertian Higher-Order Function
4.2 Contoh Penggunaan HOF pada Koleksi
4.3 HOF yang Mengembalikan Fungsi
4.4 Komposisi Fungsi
4.5 Cara Terbaik Menggunakan Lambda
5. Inline Functions dan Penggunaan inline
5.1 Pengertian Inline Function
5.2 Contoh Inline Function
5.3 Keuntungan dan Risiko Inline
5.4 Modifier Terkait: noinline dan crossinline
5.5 Praktik Terbaik Inline
6. Tail Recursion dan tailrec
6.1 Pengertian Tail Recursion
6.2 Syarat Tail Recursion
6.3 Contoh Tail Recursion
Faktorial Biasa (tidak tail-recursive)
Faktorial dengan Tail Recursion
Fibonacci dengan Tail Recursion
6.4 Manfaat Tail Recursion
6.5 Praktik Terbaik Tail Recursion
7. Extensions (Extension Functions dan Properties)
7.1 Pengertian Extension Function
7.2 Extension Properties
7.3 Extension pada Nullable Receiver
7.4 Extension pada Interface dan Companion Object
7.5 Praktik Terbaik Extension
8. Sequences dan Lazy Evaluation
8.1 Pengertian Sequence
8.2 Perbedaan List vs Sequence
8.3 Contoh Penggunaan Sequence
8.4 Manfaat Sequence
8.5 Praktik Terbaik Sequence
9. Perbandingan Paradigma Fungsional vs Imperatif dalam Kotlin
9.1 Tabel Perbandingan
9.2 Contoh Kode: FP vs Imperatif
Imperatif
Fungsional
Functional Programming