Transformasi Data dan Immutability — Paradigma Fungsional Praktis, Part 3

Bobby Priambodo
Jul 20, 2017 · 5 min read

Artikel ini adalah bagian dari sebuah seri artikel tentang Paradigma Fungsional Praktis. Silakan lompat ke akhir artikel untuk melihat navigasi keseluruhan seri ini.

Artikel ini akan membahas mengenai sebuah model pemikiran dalam pemrograman yang berorientasi transformasi data.

Transformasi data sebagai pipeline

Pada paradigma pemrograman berorientasi objek, sebuah class mengenkapsulasi gabungan dari state dan behavior.

Setiap objek dari class memiliki sebuah state dari instance variable-nya, dan memiliki operasi-operasi atau behavior khusus yang dapat dilakukan pada state tersebut melalui method. Program kita berjalan dengan membuat instance dari objek-objek tersebut dan memodifikasi state-nya melalui method yang disediakan. Ketika proyek kita sudah cukup besar dan objek yang dibuat cukup banyak, akan sulit untuk memahami state apa saja yang ada pada program kita, dan bagaimana perubahannya dari waktu ke waktu.

Pada paradigma pemrograman fungsional, state dan behavior merupakan sesuatu yang terpisah. State direpresentasikan oleh sebuah struktur data, dan behavior merupakan fungsi-fungsi yang mampu beroperasi pada data tersebut. Dalam paradigma fungsional, program kita merupakan sebuah urutan transformasi data dari suatu bentuk ke bentuk yang lain.

Menurut saya, dibanding berpikir bahwa program kita adalah kumpulan objek yang berkomunikasi satu sama lain, pendekatan pemikiran fungsional lebih cocok untuk memodelkan pemrograman secara umum. Mari lihat beberapa contoh:

  1. Untuk pemrograman back-end server, kita dapat memodelkannya sebagai proses transformasi sebuah HTTP request menjadi HTTP response.
  2. Untuk pemrograman front-end, kita dapat memodelkannya sebagai proses transformasi data dari server menjadi representasi UI.
  3. Compiler merupakan program yang mentransformasi kode sumber menjadi executable.

Beberapa bahasa seperti Elixir, OCaml, F#, dan Elm bahkan memiliki sebuah operator khusus bernama pipe (|>) yang dapat merepresentasikan transformasi data. Operator ini bisa membuat kode menjadi lebih mudah dibaca. Anda bisa menulis kode seperti:

def convert_new_lines_to_html_paragraph(text) do
"<p>" <> text <> "</p>"
|> String.replace("\n\n", "</p><p>")
|> String.replace("\n", "<br />")
end

Fungsi tersebut mengubah teks literal yang memiliki new line (\n) menjadi representasi HTML paragraph. Jika dibaca, kode tersebut menyebutkan bahwa untuk mengonversi menjadi HTML paragraph, teks input harus di konkatenasi di awal dan akhir dengan tag <p> (operator <> berfungsi untuk konkatenasi string di Elixir), kemudian ubah semua double new line menjadi tag tutup dan buka untuk paragraph, lalu ubah semua new line sisanya menjadi tag <br />. Untuk ilustrasi, potongan kode itu akan mengubah teks berikut:

Ini sebuah paragraf.

Menjadi seperti ini:

<p>Ini sebuah paragraf.</p><p>Ini paragraf kedua.<br />Sekarang memiliki lebih dari satu baris,<br />tapi masih di paragraf yang sama.</p>

Kode tersebut merupakan contoh transformasi data dari satu bentuk ke bentuk yang lain. Model pemrograman seperti ini juga sering disebut sebagai pipeline.

Tentang Immutability

Konsep transformasi data ini biasanya didukung dengan konsep data immutability — kekekalan data. Data yang kekal mengatakan bahwa kita tidak akan bisa mengubah nilainya setelah dibuat.

Tipe data String pada bahasa Java bersifat immutable; Anda tidak bisa mengubah isinya setelah melakukan assignment. Meskipun demikian, Anda dapat meng-assign ulang variabel tipe data String dengan nilai lain. Operasi-operasi seperti konkatenasi dengan +, .toUpperCase(), dan sebagainya mengembalikan nilai baru dan tidak mengubah nilai awalnya. Anda bisa melihatnya pada potongan berikut:

String s = "Halo.";
System.out.println(s.toUpperCase()); // => HALO.
System.out.println(s); // => Halo.
s = s + " Saya Bobby!"
System.out.println(s); // => Halo. Saya Bobby!

Pada baris ketiga, Anda lihat bahwa nilai s tidak berubah oleh pemanggilan s.toUpperCase(). Pada baris terakhir, kelihatannya kita mengganti nilai s, tapi sebenarnya yang kita lakukan adalah mengubah reference yang dimiliki s ke sebuah string baru hasil konkatenasi. Lebih jelasnya pada potongan berikut:

String s = "Halo."
String h = s;
s = s + " Saya Bobby!";
System.out.println(s); // => Halo. Saya Bobby!
System.out.println(h); // => Halo.

Terlihat bahwa h tidak berubah, karena ia masih me-refer objek string yang sama seperti sebelumnya.

Tapi hanya sejauh itulah data immutability di Java. Pemrograman berorientasi objek dengan Java sangat mendukung pendekatan mutasi data pada sebuah class, melalui setter-nya. Misalkan kita punya kode berikut di Java:

AnObject obj = new AnObject(1, "some param");
doSomethingOnObject(obj);
doAnotherThing(obj, "another param");

Pada kode tersebut, perhatikan bahwa kita tidak bisa tahu apakah doSomethingOnObject dan doAnotherThing akan mengubah obj atau tidak. Sama sekali tidak bisa, kecuali kita melihat implementasi fungsi masing-masing. Di dunia paralel di mana seluruh tipe data di Java immutable, begini cara kita melakukan operasinya:

AnObject obj = new AnObject(1, "some param");
AnObject newObj = doSomethingOnObject(obj);
AnObject newerObj = doAnotherThing(newObj, "another param");

Method doSomethingOnObject dan doAnotherThing secara eksplisit mengembalikan objek lain. Kita bahkan bisa reuse variabel obj kalau kita tidak butuh lagi nilai sebelumnya:

AnObject obj = new AnObject(1, "some param");
obj = doSomethingOnObject(obj);
obj = doAnotherThing(obj, "another param");

Pendekatan seperti ini menghilangkan satu kategori bug yang mungkin terjadi: Anda tidak akan bisa tidak sengaja mengubah nilai suatu variabel.

Di bahasa pemrograman “fungsional”, semua data immutable by default. Ada cara untuk membuat variabel mutable (seperti MVar pada Haskell dan ref pada OCaml), tapi Anda harus eksplisit mengenai hal itu. Semua fungsi yang bekerja pada sebuah struktur data akan mengembalikan nilai baru, bukan memutasi nilainya. Ini merupakan poin penting dalam konsep transformasi data, karena Anda akan bisa yakin bahwa data akan selalu kekal dan tidak mungkin diubah di tempat lain kecuali melalui fungsi transformasi Anda.

Jadi, bagaimana Anda bisa mengaplikasikan transformasi data dan immutability data di bahasa seperti Java?

Pertama, pisahkan antara data object dan behaviornya. Data object misalnya merupakan POJO (Plain Old Java Object) yang tugasnya hanya sebagai representasi data, sementara behavior class menggunakan data object tersebut untuk melakukan operasinya.

Kedua, pastikan fungsi yang beroperasi menggunakan data object mengembalikan objek baru dan tidak memutasi objek aslinya. Tergantung bahasa yang Anda gunakan, langkah ini mungkin perlu dilakukan dengan hati-hati. Penyalinan objek berukuran besar adalah operasi yang cukup menghabiskan waktu, bahkan ketika hanya menyalin reference, dan membuat objek terlalu banyak akan memakan memori. Bahasa pemrograman “fungsional” umumnya mengatasi ini menggunakan pendekatan yang disebut dengan persistent data structure dan structural sharing. Saya tidak akan membahasnya di sini, namun intinya pendekatan tersebut membuat proses penyalinan dan pembuatan objek baru lebih efisien.

Apa artinya kita tidak bisa melakukannya? Tentu bisa! Terdapat beberapa library yang menyediakan struktur data yang mengimplementasikan konsep immutability dengan persistent data structure dan structural sharing, seperti misalnya Functional Java dan ImmutableJS.

Itulah karakteristik ketiga yang ingin saya sampaikan dari paradigma fungsional: transformasi data. Gunakan immutability. Jangan mutasi data Anda.


Pada artikel selanjutnya, kita akan membahas satu karakteristik terakhir dari paradigma pemrograman fungsional yang ingin saya sampaikan; sebuah fitur yang merupakan intisari dari pemrograman fungsional, yaitu higher-order function—fungsi orde tinggi. Klik tautan berikut membacanya:

Atau gunakan navigasi di bawah ini untuk melihat artikel lainnya:

  1. Perkenalan Paradigma Pemrograman Fungsional Praktis
  2. Pemrograman Deklaratif — Paradigma Fungsional Praktis, Part 1
  3. Pure Functions dan Efek Samping — Paradigma Fungsional Praktis, Part 2
  4. Transformasi Data dan Immutability — Paradigma Fungsional Praktis, Part 3
  5. Higher-order Function — Paradigma Fungsional Praktis, Part 4
  6. Penerapan Paradigma Fungsional di Industri — Paradigma Fungsional Praktis, Part 5

Apakah Anda menyukai yang Anda baca? Apa saya melewatkan sesuatu? Beritahu saya melalui kolom komentar!

Paradigma Fungsional adalah sebuah blog tentang berbagai hal yang berkaitan dengan paradigma pemrograman fungsional (functional programming) di dunia pengembangan software. Tulisan-tulisan di blog ini akan dimuat dalam Bahasa Indonesia, karena menurut saya resource untuk belajar paradigma fungsional dalam bahasa kita masih sangat kurang.

Apabila Anda tertarik untuk mengetahui lebih lanjut tentang dunia paradigma fungsional, silakan ikuti blog ini!

Paradigma Fungsional

Tentang paradigma pemrograman fungsional dalam Bahasa Indonesia.

)

Bobby Priambodo

Written by

Software Engineer at Traveloka. Functional programming and distributed systems enthusiast. Java, JavaScript, Elixir, OCaml, Haskell.

Paradigma Fungsional

Tentang paradigma pemrograman fungsional dalam Bahasa Indonesia.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade