Kenapa Saya Belajar Purescript
Singkat cerita: karena mau belajar typed functional programming.
Kenapa Harus Typed?
Setelah hampir dua tahun semenjak pertama kali menggunakan Typescript, saya menyimpulkan bagaimana pentingnya static typing dalam membuat program, terutama program dalam skala yang tidak kecil. Selain dapat membantu produktifitas programmer, types juga dapat membuat code menjadi ekspresif dan relatif lebih aman saat runtime. Ekspresif dalam arti tidak perlu melakukan banyak pengecekan tipe data yang ingin dimanipulasi saat runtime. Terkadang saya temukan pengecekan tipe data ini dilakukan di tempat yang kurang tepat.
Di sini si programmer terlihat “malas”. Ia malah mencoba untuk meng-copy semua key yang ada di Tooltip.propTypes
. Yang lucu dari kasus ini adalah ternyata object Tooltip.propTypes
hanya dibuat ketika development time, alias saat production build nilainya akan menjadi undefined 😄
Walhasil aplikasi error saat runtime dan agak tricky ketika melakukan debugging. Saya terpaksa harus membuka source code Tooltip Material UI dulu baru kemudian sadar object ini bernilai undefined on production. Niatnya type-safe malah berujung bencana 🤦🏼♂️
Pernah juga saya mendapatkan code seperti ini:
Belum lagi kalau ada unit test..
Yang sebenarnya sah-sah saja, bagus malah, kalau sedang membuat library, karena gak semua user menggunakan static analysis tool seperti Typescript atau Flow sementara mereka harus tau apa jenis kesalahannya. Kalau sekedar aplikasi biasa, saya rasa pengecekan-pengecekan semacam ini bisa didelegasikan ke compiler lewat types. Kita gak perlu invent our own type-checker 🙃
Kenapa Functional Programming?
Di sini saya gak akan menjelaskan panjang-lebar kenapa kita sebagai programmer harus setidaknya mencoba belajar konsep Functional Programming — jawaban-jawaban tersebut pasti banyak temen-temen temukan di Google.
Alasan pribadi saya: Functional Programming makes more sense to me. Composition, pure functions, dan referential transparency adalah tiga kata kunci kenapa saya lebih memilih Function Programming ketimbang OOP. OOP pada umumnya menggunakan class sebagai building block untuk membangun program, dimana dengan menggunakan class sendiri saja menurut saya sudah membuka jalan untuk melakukan mutasi state yang mengakibatkan sulit tercapainya referential transparency. Debugging menjadi lebih sulit dan memaksa saya untuk benar-benar mengikuti alur program agar tidak terjadi hal-hal yang tidak diinginkan karena perubahan state. Alasan ini murni dari pengalaman pribadi dan bisa jadi subjektif ya 🙂
BTW, beberapa bahasa FP yang tidak memiliki static-typing diantaranya ada Racket, Clojure, Scheme, atau Common Lisp (dari LISP family). Elixir juga sepertinya sudah mulai banyak yang adopsi. Bahasa-bahasa ini mungkin lebih cocok untuk dipelajari bagi yang ingin mendalami Functional Programming namun tidak terlalu ingin dipusingkan dengan types.
Kenapa Purescript?
Balik ke kalimat awal: karena mau explore typed functional programming lebih dalam lagi. Bagi kamu yang belum tahu apa itu Purescript, Purescript is a strongly-typed functional programming language that compiles to JavaScript. Bahasa ini sangat terinspirasi dari Haskell dari segi syntax, paradigma, dan fitur-fitur static typing-nya. Compiler-nya bahkan ditulis menggunakan Haskell!
Ada beberapa alasan pribadi kenapa saya lebih memilih Purescript untuk mendalami typed functional programming dibandingkan dengan bahasa-bahasa lainnya:
Setup Gak Ribet
Sebelum lari ke Purescript, saya sempet belajar beberapa konsep typed FP dengan Haskell.
Kesan saya setelah beberapa kali setup project di Haskell sebagai seorang nubi itu: ribet! Banyak hal yang perlu di-setup, mulai dari pilih-pilih editor (Vim, Emacs, VS code?) sampai backend engine (Intero, HIE, GHCid?). Sempet pakai HIE juga namun semangat mulai luntur begitu language extention TemplateHaskell
diaktifkan, karena HIE langsung kejang-kejang. Build time untuk setup HIE pun bisa sampai setengah jam, memaksa laptop saya merangkap jabatan sebagai helikopter. Belum lagi disk space yang bakal banyak dimakan sama GHC 🤯
Setup projek di Purescript sendiri justru sangat friendly. Cukup jalankan
dan projek sudah langsung ready 🙂 Untuk editor saya tetap menggunakan VS Code dengan Purescript IDE sebagai pluginnya.
Jadi bagi pemula yang ingin langsung belajar typed functional programming tanpa perlu keluar effort setup sana-sini saya sarankan untuk belajar Purescript!
Community Support
Dengan komunitas yang masih belum terbilang besar, belajar Purescript tidak serta merta menjadi hal yang sulit dilakukan. Saya justru sering mengikuti diskusi-diskusi berkualitas di channel #purescript (link di sini) dari para core contributor Purescript seperti Mas Nate Faubion, Pak Harry, Om Thomas Honeyman, Pakde Harry Garrood, dan Aa’ Christoph Hegemann. Untuk pertanyaan-pertanyaan nubi, bisa ditanyakan ke channel #purescript-beginners dijamin fast-response 😉
Hindley-Milner Type System
Salah satu keunggulan dari bahasa pemrograman keluarga ML adalah penggunaan type system Hindley-Milner, dimana programmer tidak perlu memberikan type annotations di setiap variable atau function. In most cases the compiler will just infer it for you. Sebagai perbandingan sederhana, compiler Typescript belum bisa meng-infer type dari variable yang muncul di function arguments:
Di Purescript, compiler automagically tahu bahwa x
dan y
sudah pasti memiliki tipe Number dan akan memberikan pesan error jika kita masukkan, misal, sebuah string ke dalam function tersebut. Walaupun sebenarnya bisa-bisa saja membuat function tanpa type signature, namun penulisannya masih tetap disarankan sebagai bentuk “living documentation” bagi diri sendiri dan developer lain.
Dan power dalam meng-infer types ini bisa juga kita “eksploitasi” ketika otak sudah nggak bisa diandalkan lagi. Purescript mempunyai fitur type hole yang memungkinkan programmer untuk “bertanya” ke compiler apa type atau function yang cocok digunakan di bagian aplikasi tertentu. Sebagai contoh, saya punya function program
dan saya ingin compiler meng-infer type function tersebut.
Compiler akan melakukan analisis struktur program, mendeduksi types-nya dan bimsalabim jadi apa prok-prok-prok:
Tinggal di-copy-paste saja jawaban dari compiler :)) Ohiya, type hole ini tidak hanya berguna untuk mengetahui suatu type saja, tapi juga bisa dimanfaatkan untuk mencari tau function apa saja yang compatible dengan program kita. Mari gunakan code snippet yang sama dan ganti command
dengan ?help
.
Compiler akan melakukan tugasnya untuk mencari function yang cocok menggantikan placeholder ?help
. Dan bisa dilihat function command
yang kita inginkan ternyata ada di suggestion nomor 2 👀
Terima kasih compiler 🤗🤗🤗
Higher-Kinded Type dan Typeclass
Konsep ini pernah saya singgung di post tersendiri beberapa waktu yang lalu.
TL;DR: Higher-Kinded Type menyediakan kemampuan mengabstraksi type constructor sehingga code yang ditulis tidak lagi terikat dengan implementation details. Yang saya suka dari konsep ini adalah code menjadi super reusable dan generic. HKT ini jugalah yang membuat konsep Typeclass jadi lebih make sense, semua untuk mencapai code reusability.
Ada video bagus dari Nate Faubion tentang bagaimana cara me-refactor function biasa menjadi function yang generic dengan bantuan Higher-Kinded Type. Video ini sangat saya rekomendasikan bagi siapa saja yang tertarik dengan code reusability 😃
Algebraic Data Type (ADT) dan Pattern Matching
Algabraic Data Type adalah sebuah tipe data yang dibangun dari dua buah konstruksi: Sum dan Product.
Untuk Sum sendiri, ia digunakan untuk merepresentasikan suatu data yang dapat memiliki beberapa varian. Anggap saja ini sebagai “or” operator.
Sedangkan Product — kebalikan dari Sum — digunakan untuk menggabungkan dua atau lebih tipe data ke dalam satu varian. Anggap saja ini sebagai “and” operator.
Dan biasanya bahasa pemrograman yang sudah memiliki fitur ADT juga memiliki fitur Pattern Matching untuk mendekonstruksi ADT tersebut.
Saya sendiri sih suka banget sama fitur ADT terlebih ketika menyinggung ranah domain modelling, code menjadi lebih ekspresif dan mudah dibaca. Di bahasa OOP, ADT dapat diekspresikan dengan simple inheritance relationship between Parent and Child class, alias subtype polymorphism. Saya juga pernah menulis artikel tentang perbandingan antara subtyping dengan ADT beserta kelebihan dan kekurangannya.
Foreign Function Interface (FFI)
FFI memungkinkan Purescript berkomunikasi dengan bahasa target (dalam hal ini Javascript, walaupun bisa juga di-porting ke C++ atau Erlang). Dengan FFI kita bisa memanggil code di Javascript dari Purescript!
Begitupun sebaliknya, pemanggilan code Purescript dari Javascript juga mungkin dilakukan. Pembahasan lengkapnya ada di buku Purescript by Example (free).
Explicit Side Effects
Purescript sebagai pure functional language membatasi programmer dalam hal pemanggilan function yang effectful. Artinya kita tidak bisa sembarangan melakukan side-effect seperti mutasi variable, pemanggilan HTTP request, akses DOM, generate random numbers, akses Local Storage, sampai hal sesimple logging ke terminal. Hal-hal yang berpotensi mengubah “state” di luar lingkup suatu function harus dibuat eksplisit lewat type signature. Walhasil, kita sebagai programmer tahu dengan sangat jelas mana function yang pure dan mana function yang effectful, umumnya ditandai dengan Effect
monad.
Sehingga hampir “mustahil” bagi kita untuk membuat function yang effectful tanpa diketahui oleh compiler. Strictness inilah yang memaksa programmer untuk membuat program yang predictable dan sebisa mungkin effect-free agar behaviour yang tidak diinginkan dapat terminimalisir. Konsekuensinya kita harus banyak “berantem” dulu sama compiler alih-alih menghabiskan waktu untuk debugging nantinya. Hmm, sepertinya Purescript lebih memilih Correctness over Convenience..
Caveats
Kelebihan-kelebihan tersebut juga harus dibayar dengan beberapa kekurangan. Misalnya pesan error yang terkadang tidak begitu jelas. Akan sangat terasa sekali kalau sudah mulai banyak menggunakan Bind, HKT, atau function yang generic.
Dari sudut pandang saya pribadi yang “cuman mau belajar” justru lumayan menantang dan encouraging 😄, benar-benar memutar otak untuk mencari tahu why-nya dan nggak sekedar main tebak-tebakan sama compiler.
Kemudian learning resource-nya juga masih terbilang sedikit. Kebanyakan technique yang saya gunakan di Purescript justru berasal dari hasil belajar saya dengan Haskell karena kemiripan kedua bahasa ini. Hal ini bisa jadi kelebihan juga sebenarnya, if you can’t find something in Purescript then look for one in Haskell, you’ll probably end up getting the answer.
Epilog
Sejauh ini kesan saya belajar Purescript masih positif. Teman-teman bisa pantau projek sampingan saya di Github sebagai progres saya belajar Purescript. Sekian jam dari awal pengerjaan projek sampai setidaknya penulisan artikel ini (sekitar 42-an jam, tracking dari wakatime) saya belum pernah menemukan runtime error seperti “Undefined is not a function”, “Cannot convert undefined or null to object”, atau error-error lainnya yang umum didapati ketika mengembangkan aplikasi menggunakan Javascript.
Purescript is a safe language, dengan segala bentuk konsekuensinya. Static typing-nya mungkin akan sering membuat kita frustasi, tapi akan selalu berujung pada apresiasi; terutama saat runtime. Yang senang senam otak saat development time mungkin suatu saat bisa mencoba Purescript. Selamat malam ✌🏻😃