📜 You can read the English version here!
Daftar Isi
- Paradigma Pemrograman Fungsional
- Prinsip Dasar Pemrograman Fungsional
- Platform Pengembangan Erlang
- Referensi
Selamat datang di pembelajaran Elixir! Sebelum kita menulis "Hello World!" pertama, ada baiknya kita berkenalan dulu dengan paradigma pemrograman fungsional, cara berpikir yang menjadi dasar dalam bahasa ini. Saya yakin kamu yang membaca ini mungkin berasal dari latar belakang seperti JavaScript, TypeScript, atau Ruby, dan sudah terbiasa dengan gaya pemrograman imperatif serta berorientasi objek. Di Elixir, kita akan melihat pemrograman dari sudut yang sedikit berbeda, di mana kita tidak menggunakan class atau object, melainkan membangun program melalui kumpulan function yang saling bekerja sama.
Paradigma Pemrograman Fungsional
Paradigma pemrograman adalah cara berpikir dalam menulis kode. Paradigma menentukan bagaimana kita memberi instruksi kepada komputer, mengolah data, dan menyelesaikan masalah.
Setiap bahasa pemrograman umumnya mendukung satu atau lebih paradigma. Pemilihan paradigma yang tepat membantu kita menulis kode yang lebih mudah dibuat, dibaca, dan dipelihara.
Dalam praktiknya, terdapat beberapa paradigma yang umum digunakan, seperti imperative, declarative, object-oriented, dan functional. Berikut penjelasannya.
Imperative Programming
Paradigma imperatif merupakan pendekatan tradisional dalam menulis program. Di sini kita menjelaskan secara terperinci bagaimana komputer harus menyelesaikan suatu tugas, langkah demi langkah. Analogi sederhananya seperti mengikuti resep masakan yang dijalankan secara berurutan.
Contoh dalam Ruby:
total = 0
(1..5).each do |i|
total += i
end
puts "Total: #{total}"
Kode ini menghitung jumlah angka dari 1 hingga 5 dengan menambahkan nilainya ke variabel total secara bertahap. Paradigma ini banyak digunakan di bahasa populer seperti Ruby, JavaScript, Python, dan PHP.
Declarative Programming
Paradigma deklaratif menekankan pada apa yang ingin dicapai, bukan bagaimana cara mencapainya. Kita cukup menyatakan hasil yang diinginkan, sementara komputer atau runtime menentukan langkah-langkah pelaksanaannya.
Contoh dalam SQL:
SELECT * FROM users WHERE is_active = 1;
Kita hanya menyebutkan tujuannya, mengambil semua pengguna aktif tanpa perlu menjelaskan proses pencariannya.
Contoh serupa dalam Elixir:
numbers = [1, 2, 3, 4, 5]
Enum.sum(numbers)
Kita hanya menyatakan bahwa ingin menjumlahkan seluruh elemen numbers, dan function Enum.sum menangani detail prosesnya. Pendekatan deklaratif juga banyak ditemukan di HTML, CSS, dan regular expressions. Dalam Elixir, gaya ini berpadu dengan paradigma fungsional untuk menghasilkan kode yang ringkas dan ekspresif.
Object-Oriented Programming (OOP)
Paradigma berorientasi objek menyusun program dalam bentuk object, yaitu representasi dari entitas nyata atau konsep abstrak. Setiap object memiliki data (state) dan perilaku (method).
Bayangkan robot mainan: setiap robot memiliki status internal seperti energi baterai (state) dan aksi yang bisa dilakukan (behavior). Ketika kita memberi perintah, robot merespons sesuai kondisinya.
Konsep utama dalam OOP meliputi:
- Encapsulation: menggabungkan data dan perilaku dalam satu kesatuan.
- Inheritance: memungkinkan object mewarisi sifat dan perilaku dari object lain.
- Polymorphism: instruksi yang sama dapat dijalankan dengan cara berbeda oleh object yang berbeda.
Contoh dalam Ruby:
class Robot
attr_accessor :name, :battery
def initialize(name, battery = 100)
@name = name
@battery = battery
end
def move_forward
if @battery > 0
puts "#{@name} is moving forward"
@battery -= 10
else
puts "#{@name} has no energy left!"
end
end
end
r1 = Robot.new("Alpha")
r1.move_forward
Kita membuat class Robot dengan atribut name dan battery, lalu menambahkan perilaku move_forward. Saat objek r1 dipanggil untuk bergerak, ia menyesuaikan perilaku berdasarkan status baterainya.
Functional Programming (FP)
Pemrograman fungsional berfokus pada penyusunan program dari banyak function kecil yang masing-masing menjalankan satu tugas spesifik. Function tersebut kemudian dikombinasikan untuk membentuk alur kerja yang lebih kompleks.
Karakteristik utama FP:
- Pure function: selalu menghasilkan keluaran yang sama untuk input yang sama tanpa efek samping (side effect).
- Immutability: data bersifat tetap dan tidak diubah setelah dibuat.
Alih-alih memodifikasi data, FP biasanya membuat data baru menggunakan function seperti map atau filter.
Contoh dalam Elixir:
numbers = [1, 2, 3, 4, 5]
numbers
|> Enum.filter(fn x -> rem(x, 2) == 0 end)
|> Enum.map(fn x -> x * 2 end)
Kode ini mengambil angka genap dari numbers, lalu menggandakan nilainya. Variabel numbers tetap tidak berubah karena setiap operasi menghasilkan daftar baru.
Paradigma di Erlang dan Elixir
Erlang dan Elixir menggabungkan paradigma deklaratif dan fungsional. Alih-alih menulis instruksi langkah demi langkah, kita cukup mendeskripsikan tujuan program, sedangkan detail eksekusinya ditangani oleh runtime. Semua data bersifat immutable, dan logika program dibangun dari kumpulan pure functions yang saling berinteraksi.
Pendekatan ini membuat program lebih mudah diuji, lebih aman dari efek samping, dan secara alami mendukung concurrency (menjalankan banyak proses secara paralel). Inilah alasan mengapa Elixir dan Erlang sangat efektif untuk sistem berskala besar dan berdaya tahan tinggi (fault-tolerant systems).
Prinsip Dasar Pemrograman Fungsional
Functional programming adalah paradigma yang berfokus pada penyusunan program dari banyak function kecil, di mana masing-masing function menangani satu tugas spesifik. Function tersebut kemudian digabungkan untuk membentuk alur kerja yang lebih kompleks.
Dalam paradigma ini, program dipandang sebagai rangkaian transformasi data, sebuah function menerima input, mengolahnya, lalu menghasilkan output baru tanpa mengubah data asli.
Pendekatan ini berbeda dengan gaya imperative, di mana program ditulis sebagai urutan instruksi yang mengubah state (keadaan) dari waktu ke waktu. Dalam imperative programming, fokusnya adalah bagaimana langkah-langkah dijalankan, sedangkan dalam functional programming fokusnya ada pada hasil dari transformasi data.
Dengan merangkai function dan mengalirkan hasilnya ke function berikutnya, alur pemrosesan data menjadi lebih jelas, mudah dibaca, serta aman dari efek samping. Pendekatan ini juga mempermudah pemeliharaan dan pengujian kode.
Paradigma ini dibangun di atas beberapa prinsip utama berikut.
Immutability
Dalam pemrograman fungsional, data bersifat immutable, artinya setelah dibuat nilainya tidak dapat diubah. Jika kita ingin "mengubah" data, yang sebenarnya terjadi adalah pembuatan salinan baru dengan nilai berbeda, sementara data asli tetap utuh. Proses ini disebut transformasi data, bukan modifikasi.
Contoh dalam Elixir:
list = [1, 2, 3]
new_list = [0 | list] # Hasil: [0, 1, 2, 3]
Variabel list tetap berisi [1, 2, 3]. Saat kita menambahkan 0 di depan, Elixir membuat list baru bernama new_list tanpa mengubah data lama.
Sekilas, immutability tampak kurang efisien karena membuat data baru alih-alih mengubah yang sudah ada. Namun, bahasa fungsional modern seperti Elixir menggunakan berbagai optimisasi di balik layar, sehingga hanya bagian data yang benar-benar berubah yang disalin.
Keunggulan immutability antara lain:
- Data lebih aman, karena tidak bisa diubah secara tidak sengaja.
- Alur program lebih mudah dipahami, sebab nilai tidak berubah di tengah proses.
- Risiko bug berkurang, terutama akibat perubahan tak terduga.
- Mendukung eksekusi paralel, karena setiap proses bekerja pada salinannya sendiri.
Pure Function
Pure function adalah function yang selalu menghasilkan output yang sama untuk input yang sama dan tidak menimbulkan efek samping. Dua ciri utamanya adalah:
- Deterministik: hasilnya selalu sama untuk input yang sama.
- Bebas efek samping: tidak mengubah state di luar dirinya dan tidak berinteraksi langsung dengan lingkungan eksternal.
Contoh dalam Elixir:
square = fn x -> x * x end
Kode di atas mendefinisikan anonymous function bernama square yang selalu mengembalikan kuadrat dari inputnya. Jika kita memanggil square.(2), hasilnya pasti 4 tanpa variasi atau pengaruh dari luar.
Keuntungan pure function:
- Mudah diuji, cukup beri input dan periksa output.
- Mudah dirangkai (function composition), dapat digabung untuk membentuk alur transformasi data.
- Mudah dipahami, karena perilakunya dapat diprediksi.
Tentu, tidak semua bagian program bisa sepenuhnya pure. Interaksi dengan file, basis data, atau jaringan pasti menimbulkan efek samping. Prinsip utamanya adalah memisahkan logika pure dari bagian yang berinteraksi dengan dunia luar. Dalam Elixir, efek samping biasanya ditangani di lapisan seperti controller, sedangkan logika inti dijaga tetap murni.
First-Class Function
Dalam paradigma fungsional, function diperlakukan sebagai warga kelas satu (first-class citizen). Artinya, function memiliki kedudukan yang sama dengan data biasa sehingga dapat:
- Disimpan dalam variabel.
- Diteruskan sebagai argumen ke function lain.
- Dikembalikan sebagai hasil dari function lain.
Contoh dalam Elixir:
double = fn x -> x * 2 end
result = Enum.map([1, 2, 3], double) # result: [2, 4, 6]
Penjelasannya:
-
doubleadalah anonymous function yang menggandakan nilai input. -
Function
doublediteruskan sebagai argumen keEnum.map. -
Enum.mapmemanggildoubleuntuk setiap elemen dalam[1, 2, 3]. - Hasilnya adalah list baru
[2, 4, 6].
Hal ini menjadi dasar bagi tiga konsep penting dalam pemrograman fungsional yang akan kita pelajari nanti:
- Lambda (anonymous function): function tanpa nama yang biasanya singkat dan dipakai langsung.
- Closure: function yang membawa konteks lingkungannya sehingga tetap dapat mengakses variabel luar meskipun dijalankan di tempat lain.
- Higher-Order Function (HOF): function yang menerima function lain sebagai argumen atau mengembalikannya sebagai hasil.
Recursion
Rekursi adalah teknik di mana sebuah function memanggil dirinya sendiri dengan versi masalah yang lebih kecil hingga mencapai kondisi berhenti.
Dalam pemrograman fungsional, rekursi menggantikan looping. Bahasa seperti Elixir tidak menyediakan for atau while tradisional karena data bersifat immutable, sehingga variabel penghitung tidak bisa diubah di setiap iterasi. Solusinya adalah memanggil kembali function yang sama hingga kondisi berhenti tercapai.
Contoh sederhana mencetak $n$ bilangan asli pertama:
def print(1), do: IO.puts(1) ❶
def print(n) when n > 1 do ❷
print(n - 1)
IO.puts(n)
end
Penjelasannya:
- ❶ Kasus dasar
n = 1menjadi titik berhenti rekursi. - ❷ Kondisi kedua memanggil
printdengann - 1, lalu mencetak nilainsetelah rekursi selesai.
Jika kita memanggil print(3), hasilnya:
1
2
3
Lazy Evaluation
Lazy evaluation adalah teknik menunda eksekusi perhitungan hingga hasilnya benar-benar dibutuhkan. Program tidak langsung menghitung semua nilai, melainkan menyiapkan "rencana" perhitungan. Nilai baru dihitung hanya saat diperlukan.
Keunggulan lazy evaluation:
- Lebih efisien, karena hanya menghitung nilai yang dibutuhkan.
- Dapat menangani data besar atau tak terbatas, karena tidak harus membuat seluruh struktur data sekaligus.
Contoh dalam Elixir menggunakan Stream, yang bekerja secara lazy:
stream = Stream.map([1, 2, 3], fn x -> x * 2 end) ❶
Enum.sum(stream) ❷
Penjelasannya:
- ❶
Stream.maptidak langsung menggandakan elemen[1, 2, 3], tetapi menyiapkan rencana perhitungan. - ❷ Saat
Enum.sumdipanggil, barulah perhitungan dijalankan. Elemen digandakan menjadi[2, 4, 6], lalu dijumlahkan menghasilkan12.
Platform Pengembangan Erlang
Sebelum mempelajari Elixir, penting untuk memahami platform tempat Elixir berjalan, yaitu Erlang. Elixir dibangun di atas ekosistem Erlang. Memahami lapisan dasarnya membantu kita melihat mengapa Elixir sangat kuat untuk membangun sistem dengan ketersediaan tinggi.
Apa Itu Erlang
Erlang adalah bahasa pemrograman sekaligus platform pengembangan yang dirancang untuk membuat sistem dengan ketersediaan tinggi (high availability). Nama "Erlang" diambil dari Agner Krarup Erlang, matematikawan Denmark yang mengembangkan teori antrian (queueing theory) dalam telekomunikasi. Nama ini juga sering dianggap sebagai singkatan dari Ericsson Language.
Sejarah Singkat Erlang
Erlang dikembangkan pada pertengahan 1980-an oleh Ericsson Computer Science Laboratory di Swedia. Tiga tokoh utamanya adalah Joe Armstrong, Robert Virding, dan Mike Williams. Tujuan mereka sederhana tetapi ambisius, yaitu membangun sistem telekomunikasi yang tidak pernah berhenti berjalan, bahkan saat terjadi lonjakan panggilan, kesalahan perangkat lunak, atau kegagalan perangkat keras.
Sistem jaringan telepon Ericsson harus beroperasi tanpa henti selama 24 jam. Satu kegagalan kecil dapat memutus layanan bagi ribuan pengguna.
Pada tahun 1998, Erlang dirilis sebagai perangkat lunak open source. Sejak itu, Erlang digunakan dalam berbagai sistem besar seperti WhatsApp, Discord, RabbitMQ, sistem finansial, hingga backend gim multiplayer. Selama lebih dari tiga dekade, Erlang terbukti stabil, andal, dan mampu menangani skala besar. Inilah kekuatannya, membangun sistem yang terus hidup (high availability systems).
Sistem dengan Ketersediaan Tinggi (High Availability System)
Joe Armstrong pernah berkata dalam sebuah wawancara pada tahun 2013:
"Kalau Java itu tulis sekali, jalankan di mana saja. Erlang itu tulis sekali, jalankan selamanya."
Ungkapan ini merangkum filosofi Erlang, membangun sistem yang tetap berjalan meskipun terjadi kegagalan. Untuk mencapainya, Erlang memiliki beberapa kemampuan penting:
- Fault Tolerance: proses yang gagal tidak menjatuhkan seluruh sistem. Proses rusak dapat dibuat ulang secara otomatis, dan efek kegagalannya dibatasi secara lokal.
- Scalability: sistem dapat diperluas tanpa menghentikan layanan, baik dengan menambah sumber daya (vertical scaling) maupun menambah node baru (horizontal scaling).
- Distributed System: sistem dapat dijalankan di banyak mesin. Jika satu mesin gagal, mesin lain mengambil alih tanpa menghentikan layanan.
- Responsiveness: sistem tetap responsif meskipun beban meningkat. Proses berat tidak memblokir proses lain.
- Hot Code Swapping: aplikasi dapat diperbarui tanpa waktu henti, misalnya memperbarui perangkat lunak telepon tanpa memutus panggilan aktif.
Semua kemampuan ini dimungkinkan oleh mesin virtual Erlang dan model konkurensi yang digunakannya.
Erlang Process
Untuk mencapai ketersediaan tinggi, Erlang menggunakan mesin virtual BEAM (Bogdan/Björn’s Erlang Abstract Machine) serta model konkurensi berbasis proses. Erlang process adalah unit eksekusi terkecil di Erlang. Ia berbeda dari thread sistem operasi karena setiap proses sepenuhnya terisolasi, tidak berbagi memori, dan berkomunikasi melalui pengiriman pesan (message passing).
Model ini memungkinkan satu sistem Erlang menjalankan ribuan bahkan jutaan proses secara bersamaan.
Keunggulan model proses Erlang:
- Fault Tolerance: kegagalan satu proses tidak memengaruhi proses lain. Supervisor process mengawasi proses lain dan dapat membuat ulang proses yang gagal secara otomatis.
- Scalability: komunikasi berbasis pesan menghindari kerumitan sinkronisasi. BEAM menjadwalkan proses ke seluruh inti CPU secara otomatis.
- Distribution: proses dapat berkomunikasi dengan proses lain di mesin mana pun dengan cara yang sama tanpa memedulikan lokasi fisiknya.
- Responsiveness: BEAM menggunakan preemptive scheduler yang memastikan tidak ada proses yang mendominasi CPU. Pengumpulan sampah (garbage collection) dilakukan per proses sehingga cepat dan tidak mengganggu keseluruhan sistem.
Model inilah yang membuat sistem Erlang tetap hidup meskipun terjadi kegagalan parsial.
Penerapan Nyata dalam Sistem Server-side
Kekuatan Erlang paling terasa pada sistem server-side, yaitu sistem yang berjalan di server untuk melayani banyak pengguna secara terus-menerus. Contoh utamanya adalah aplikasi chat, sistem telekomunikasi, dan backend layanan waktu nyata (real-time backend).
Sistem server-side biasanya terdiri atas beberapa komponen yang tersebar di banyak mesin (node). Erlang memudahkan pembangunan sistem seperti ini dengan menyediakan:
- Konkurensi bawaan untuk menangani ribuan hingga jutaan koneksi.
- Distribusi lintas mesin dengan komunikasi pesan yang seragam.
- Mekanisme failover yang menjaga layanan tetap berjalan ketika sebagian node gagal.
Contoh Kasus Perbandingan Dua Web Server
Dalam salah satu proyeknya, Saša Jurić, penulis buku Elixir in Action yang menjadi rujukan utama seri ini, membandingkan dua pendekatan dalam membangun web server dengan kebutuhan teknis berikut:
- Melayani banyak klien.
- Menangani permintaan dengan waktu proses panjang (long-running requests).
- Mengelola server-wide state.
- Menyimpan data persisten.
- Menjalankan background jobs.
Hasil perbandingan menunjukkan perbedaan yang mencolok:
- Server A membutuhkan tumpukan teknologi beragam seperti NGINX, Ruby on Rails, Redis, MongoDB, cron, dan Bash.
- Server B mampu menangani seluruh kebutuhan tersebut langsung di dalam platform Erlang.
| Kebutuhan Teknis | Server A | Server B |
|---|---|---|
| HTTP Server | NGINX, Phusion Passenger | Erlang |
| Request Processing | Ruby on Rails | Erlang |
| Long-running Requests | Go | Erlang |
| Server-wide State | Redis | Erlang |
| Persistable Data | Redis, MongoDB | Erlang |
| Background Jobs | cron, Bash, Ruby | Erlang |
| Service Crash Recovery | Upstart | Erlang |
Perbandingan ini menunjukkan bahwa Erlang menghadirkan solusi yang lebih ringkas. Sebagian besar kebutuhan sistem, mulai dari pengelolaan koneksi, state, hingga pemulihan kegagalan, dapat ditangani langsung oleh platform tanpa integrasi kompleks antar teknologi.
Namun, Erlang tidak dimaksudkan untuk menggantikan seluruh teknologi populer yang sudah ada. Ia lebih tepat dipandang sebagai platform kuat untuk membangun sistem lengkap dengan kompleksitas awal yang rendah, namun tetap memungkinkan integrasi dengan bahasa atau layanan lain bila diperlukan.
Singkatnya, Erlang memberikan fleksibilitas tinggi bagi developer. Ia dapat digunakan sebagai platform mandiri atau diintegrasikan dengan teknologi lain sesuai kebutuhan sistem.
Erlang sebagai Platform Pengembangan
Erlang bukan hanya bahasa pemrograman, tetapi platform lengkap yang terdiri atas beberapa komponen utama:
- Bahasa Erlang: bahasa fungsional untuk menulis program yang dijalankan di mesin BEAM.
- Mesin Virtual BEAM: mengeksekusi bytecode, mengatur proses, komunikasi antarproses, serta menjaga sistem tetap berjalan meskipun terjadi kesalahan.
- OTP (Open Telecom Platform): kumpulan pustaka dan pola desain siap pakai untuk membangun sistem Erlang. OTP mencakup manajemen proses, pengawasan, pembaruan kode tanpa henti, dan pemantauan sistem. OTP sering disebut bersama Erlang sebagai Erlang/OTP.
- Tooling: berbagai alat bantu untuk menjalankan, mengelola, dan berinteraksi dengan program Erlang beserta dependensinya.
Erlang/OTP bersifat open source, tersedia di berbagai sistem operasi utama, dan masih aktif dikembangkan oleh Ericsson serta komunitas melalui repositori resmi Erlang/OTP di GitHub.
Referensi
- Saša Jurić. (2024). Elixir In Action (3rd ed.). Manning Publications.
- IONOS Editorial Team. (2021, May 21). Imperative Programming. https://www.ionos.com/digitalguide/websites/web-development/imperative-programming
- Federico Pereiro. (n.d.). Declarative Programming: Is It A Real Thing? Diakses Mei 23, 2024, dari https://www.toptal.com/software/declarative-programming
Top comments (0)