- Karakteristik bahasa dan ekosistem OCaml sangat baik, serta cocok untuk proyek pribadi maupun profesional
- Berbagai paradigma dan fitur lanjutan seperti sistem tipe statis, tipe aljabar, sistem modul, model objek, dan efek yang ditentukan pengguna terintegrasi secara stabil
- Tersedia toolchain yang matang seperti package manager OPAM, sistem build Dune, dukungan editor LSP/Merlin, dan alat dokumentasi Odoc, serta ekosistem library yang beragam untuk web, blockchain, tooling, dan lainnya
- Komunitasnya memiliki aksesibilitas, keramahan, dan profesionalisme, sehingga memudahkan pembelajaran dan kolaborasi, serta prospeknya juga cerah berkat evolusi yang konsisten
Alasan memilih OCaml sebagai bahasa utama
- Penulis telah lama menggunakan berbagai bahasa pemrograman, dan di antaranya memilih OCaml sebagai bahasa utama
- Keunggulan terbesar OCaml adalah sistem tipe statis yang kuat serta dukungan pemrograman fungsional yang lebih baik dibandingkan C atau bahasa fungsional lainnya
- Berkat sistem tipe tersebut, penulis mengalami banyak manfaat dalam pencegahan bug dan optimasi kode
- Dalam berbagai proyek pengembangan nyata, penggunaan OCaml secara signifikan meningkatkan produktivitas dan stabilitas
Keunggulan OCaml dan penerapan praktis
- Sebagian besar kode dapat ditulis dengan cepat, dan penggunaan komposisi fungsi serta data immutable meningkatkan keamanan
- Belakangan ini, ekosistem dan alat bantu OCaml (IDE, sistem build, dan sebagainya) juga terus berkembang
- Berkat beragam library dan paket eksternal, pengembangan yang efisien menjadi memungkinkan dalam pekerjaan nyata
- Dibandingkan dengan Python atau Java, OCaml memang kurang terkenal, tetapi sangat kuat dari sisi produktivitas, keamanan, dan fleksibilitas
Karakteristik bahasa
- Berasal dari penelitian namun dipadukan dengan penerapan industri, sehingga pengembangan fiturnya berfokus pada ekspresivitas dan keamanan
- Fitur modern seperti efek yang ditentukan pengguna dan affine session
- Pemeriksaan tipe statis berfungsi sebagai jaring pengaman sekaligus alat desain, dan membantu menghapus kesalahpahaman akibat pengalaman tipe yang buruk
- Multi-paradigma: fungsional, imperatif, modular, berorientasi objek, dan dukungan multicore
- Sintaks keluarga ML ringkas dan konsisten, serta ada sintaks alternatif seperti ReasonML
- Tipe aljabar (produk, jumlah, eksponensial), pattern matching, dan polimorfisme memberikan keunggulan dalam pemodelan data/domain
- Sistem modul mendukung pemisahan antarmuka dan implementasi, abstraksi, penggunaan ulang, hingga polimorfisme tingkat lanjut
- Dependency inversion: menyediakan cara injeksi yang fleksibel melalui modul/efek
Ekosistem dan tooling
- Target kompilasi: native, bytecode, JavaScript(
Js_of_ocaml,Melange), WebAssembly - MirageOS menyediakan disiplin penulisan library multi-konteks
- OCaml Platform:
- OPAM: manajemen versi, switch, indeks paket, dukungan CI
- Dune: build cepat, konfigurasi S-expression, penyederhanaan rilis melalui
dune-release - LSP/Merlin: pelengkapan kode, navigasi, dan formatting di VSCode, Emacs, dan lainnya
- Odoc: mendukung referensi silang, halaman manual, doctest, dan lain-lain
- Library yang kaya: web (Dream, Ocsigen), blockchain dan kriptografi (HACL*), pengujian (alcotest, qcheck, dll.)
- Library standar memang kecil, tetapi ada alternatif seperti Batteries, Base/Core, dan Containers
Tantangan baru dan komunitas
- Komunitas OCaml memang kecil, tetapi terus berkembang, dan menunjukkan arah yang ramah bagi pengguna
- Bagi pengembang yang ingin menantang diri dengan bahasa atau paradigma baru, OCaml sangat layak dipelajari secara mendalam
- Banyak pengguna menyebut bahwa pengalaman memakai OCaml memberi sudut pandang baru dan meningkatkan kemampuan pemecahan masalah
Kesimpulan
- OCaml adalah bahasa pemrograman yang kuat yang tidak terbatas pada bidang tertentu (misalnya keuangan, compiler, atau pengembangan sistem), melainkan dapat digunakan secara umum
- Efisiensi, kemudahan pemeliharaan, dan kemampuan mencegah masalah yang diperoleh di praktik nyata membuktikan nilainya di lapangan
- Meski mungkin kurang dikenal dibanding bahasa atau tren terbaru, jika Anda mengutamakan keandalan dan keamanan, ini tetap merupakan pilihan yang sangat layak dipertimbangkan
2 komentar
Saya sempat menangani OCaml waktu kuliah pascasarjana, tetapi ekosistemnya benar-benar minim, referensinya juga nyaris tidak ada, dan terutama tidak ada orang yang bisa ditanyai. Menurut standar pribadi saya, di Korea OCaml praktis nyaris tidak dipakai kecuali mungkin di kalangan asosiasi bahasa pemrograman. COBOL mungkin pernah didengar, tetapi OCaml kemungkinan besar belum.
Komentar Hacker News
Saya pernah melihat presentasi tentang pengalaman Google memperkenalkan Rust ke tim Android. Ada dua hal yang mengesankan. Pertama, karena berbagai proyek dipindahkan dari Python ke Rust, sepertinya performa bukan isu sebesar itu. Kedua, fitur yang paling disukai pengguna Rust justru hal-hal mendasar seperti pattern matching dan ADT (Algebraic Data Types). Jadi, rasanya kontribusi terbesar Rust bukanlah fitur unik seperti lifetime, melainkan hal-hal yang sudah disediakan bahasa keluarga ML sejak 1990-an. Kalau saja OCaml sekitar 2010 sudah membereskan ketidaknyamanan seperti multicore, sepertinya popularitasnya bisa setara Rust. Sayangnya, OCaml terjebak di jurang antara akademia dan industri. Tambahan satu hal: integer 31-bit cukup merepotkan secara praktis saat melakukan operasi bit, dan dari sisi estetika saya benar-benar tidak suka tanda titik koma ganda.
Menurut saya, OCaml saat itu sebenarnya sudah cukup bagus. Pada 2010 saya memakainya secara profesional dan jauh lebih nyaman daripada Python. Lihat saja apa yang sudah dicapai JaneStreet. Penyebab terbesar OCaml tidak diadopsi luas menurut saya adalah karena bahasa ini tidak dibuat atau dipimpin dari Amerika Serikat. Kita ingin percaya bahwa popularitas bahasa datang dari keunggulan teknis, tetapi pada akhirnya ini soal tren. Alasan Rust berhasil secara massal juga karena promosi besar-besaran dan aktivitas komunitas yang agresif. Mereka bahkan punya staf khusus untuk aktif memperkenalkan namanya.
Google berusaha menjaga daftar bahasa resmi yang boleh dipakai untuk kode layanan produksi tetap sesingkat mungkin. Rust tampaknya dipilih karena bisa menggantikan atau melengkapi C++. Sulit bagi OCaml untuk berada di posisi itu (mungkin bisa menggantikan Go, tapi kemungkinannya kecil). Jadi alasan terbesar Rust dipilih mungkin karena dia satu-satunya bahasa resmi yang menyediakan ADT, bukan karena kecepatan build tidak dianggap penting. Wajar juga kalau OCaml tidak bisa menggantikan Rust. Bahasa dengan GC sudah banyak, seperti Go dan Haskell, sedangkan sekitar 2010 satu-satunya bahasa yang cukup ekspresif untuk membidik bare metal hanyalah C++ (dan itu pun sebelum C++11/C++17 malah lebih buruk lagi).
Sangat setuju. Kalau OCaml memperbaiki beberapa masalah kecil, ia benar-benar bisa jadi pemain penting. Kecepatan build-nya bahkan sekarang masih jauh lebih cepat daripada Rust. Namun OPAM (manajer paket) sering bermasalah dan terkenal membingungkan. Dukungan Windows sangat buruk, sampai bisa dibilang parah. Bahkan lebih buruk daripada dukungan Windows pada Perl dulu. Dokumentasi resmi terlalu ringkas sampai nyaris tidak berguna. Sintaksnya sendiri juga sulit dipahami, dan pesan seperti separuh file berisi syntax error gara-gara typo kecil juga sering muncul. Sintaks gaya C yang sudah dikenal Rust jauh lebih mudah. Singkatnya, kelebihan OCaml hanya build yang cepat, dan itu saja belum cukup memberi alasan kuat untuk memakainya.
Karena itu, kalau saya ingin menulis dengan gaya ML, saya akan lebih dulu mencari Kotlin, Scala, atau F# ketimbang Rust. Dan sekarang bahkan Java atau C# juga sudah mengadopsi cukup banyak unsur ML sehingga tidak terlalu terasa asing. Saya sudah terbiasa dengan type system ML sejak era Caml Light dan Objective Caml, jadi melihat orang-orang begitu antusias pada Rust belakangan ini kadang terasa seperti mereka mengira Rust baru saja membawa type system ML ke dunia.
Soal pendapat bahwa OCaml seharusnya bisa lebih siap, menurut saya justru keuntungan terbesarnya adalah adanya beragam pilihan bahasa. Di Inggris saja, meski populasinya lebih kecil, hidup sangat banyak bahasa yang berbeda. Misalnya Cornish, bahasa mati di Eropa, belakangan dihidupkan kembali oleh penduduk setempat, dan di kalangan penggembala masih ada bahasa berhitung bernama Cubric. Saya sendiri mulai memakai program Geneweb berbasis OCAML untuk silsilah keluarga generasi berikutnya (pindahan dari aplikasi Windows bernama TMG). Data keluarga saya memuat sampai 140 ribu orang. Karena Geneweb dibuat dengan OCAML, minat saya pada bahasa ini pun tumbuh. Kalau bahasa pemrograman terasa sulit, saya sarankan coba mendalami genealogi. Tidak lama lagi Anda akan pusing gara-gara standar bernama GEDCOM.
OCaml adalah salah satu bahasa yang saya cintai. Pekerjaan terbesar yang saya lakukan dengannya adalah membangun aplikasi CRUD 100% untuk organisasi Writer's Festival menggunakan OCaml (JSX berbasis ReasonML), Dream, HTMX, dan DataTables. Saya sangat puas karena bisa memakai modul untuk mendaur ulang template frontend, dan saat ada perubahan pada model data, compiler langsung menunjukkan bagian mana yang rusak. Saya juga berhasil memindahkan data Excel ke DB yang layak, mengekspor jadwal berbasis template format .odt atau langsung menjadi file zip tanpa harus melewati disk server, dan banyak hal lain yang mengejutkan bisa diwujudkan dalam ekosistem OCaml. Namun saya harus menulis semua query DB sebagai string dan melakukan konversi tipe secara manual, jadi rasanya sangat melelahkan (tidak ada type check saat compile time). Sistem autentikasi pun harus dibuat sendiri, sehingga terlalu banyak waktu habis untuk hal-hal yang bukan pengembangan produk inti. Setelah melihat-lihat berbagai bahasa, saya merasa tidak ada bahasa yang sempurna. Setiap bahasa punya kekurangan khasnya sendiri. Sekarang saya sedang membuat aplikasi pribadi dengan Rails, dan jauh lebih puas karena hampir semua yang saya butuhkan tersedia sebagai bawaan, sehingga saya bisa fokus pada pekerjaan inti seperti desain layout atau deployment yang sesungguhnya, bukan pada bahasanya.
DarkLang awalnya dikembangkan dengan OCaml, lalu belakangan beralih ke F#. Alasan utamanya adalah ekosistem library dan concurrency (tulisan terkait). Saya memang cukup akrab dengan .NET jadi mungkin agak bias, tetapi untuk bagian-bagian yang membosankan pun sudah tersedia banyak pilihan, sehingga saya bisa fokus pada masalah yang esensial. Saya cukup lama memakai F# secara profesional dan bahkan memelihara library UI yang populer, tetapi karena ekosistem bahasanya kecil, bahkan di .NET pun solusi tidak selalu langsung tersedia. Jadi kalau memilih bahasa yang tidak arus utama (misalnya F# alih-alih C#), perlu diingat ada biaya yang harus dibayar. OCaml juga sama: ia memberi bahasa yang kuat, tetapi karena berada di luar arus utama, ada banyak ketidaknyamanan. Beberapa perusahaan memang memakainya di layanan produksi, tetapi itu biasanya kasus yang disesuaikan dengan kebutuhan mereka yang unik.
Saya sudah beberapa tahun berusaha menyukai OCaml, tetapi bagian yang paling tidak nyaman adalah "tidak bisa mencetak objek sembarang". Dengan ppx kita bisa menurunkan fungsi
to_stringsecara otomatis, tetapi konfigurasinya merepotkan dan usability-nya kalah dari Rust. Untuk mencetak tipe seperti Set atau Map juga perlu kerja tambahan (contoh referensi). Di golang, hampir semua hal bisa dicetak dengan mudah lewat formatting%v, sedangkan di OCaml tangan kita jadi lebih banyak bekerja.%vdi Go juga tidak sempurna, dan untuk menelusuri pointer lebih dalam tetap butuh library terpisah seperti go-spew. Pendekatan__repr__di Python adalah yang paling nyaman dari semua yang pernah saya lihat.Saya belum pernah memakai OCaml langsung, tetapi pengalaman bekerja dengan F# sangat menyenangkan. Di era LLM seperti sekarang, rasanya menarik untuk kembali melirik bahasa fungsional. Dalam paradigma fungsional seperti OCaml atau Haskell, informasi bisa dipadatkan secara efisien ke dalam teks yang kecil, jadi mungkin lebih banyak makna bisa dimasukkan ke context window LLM. Mungkin juga layak diuji apakah perubahan yang lebih kompleks bisa diterapkan sekaligus dibandingkan dengan Java, C#, atau Ruby.
Saya awalnya juga mengira begitu, tetapi berubah pikiran setelah benar-benar bekerja pada codebase Haskell besar. Mungkin karena FP kurang terwakili di dataset pelatihan, bahasa yang lebih ringkas justru terasa kurang cocok untuk LLM. Kodenya yang lebih verbose memberi LLM lebih banyak kesempatan untuk membetulkan diri setelah memprediksi token yang salah, sehingga hasil akhirnya terasa menghasilkan kode yang lebih benar.
Dalam eksperimen pribadi saya, saya pernah membuat game CLI sederhana dengan C++ dan Haskell. Jumlah baris di Haskell memang lebih sedikit, tetapi jumlah katanya hampir sama, jadi kodenya hanya terlihat lebih "melebar". Saya belum membandingkannya dengan Java atau bahasa yang lebih eksplisit, tetapi menurut saya gaya yang tepat bergantung pada sifat programnya. Ada yang lebih cocok imperatif, ada yang lebih cocok fungsional.
Kalau kemampuan LLM menghasilkan kode sedikit saja lebih maju, saya ingin sekali bisa membatasi ruang gerak kode dengan type system dan effect system yang sangat kuat. Misalnya, dengan dependent types, kita bisa memeriksa saat compile time bahwa "fungsi ini pasti mengembalikan list yang sudah terurut" atau "fungsi ini pasti mengembalikan solusi Sudoku yang valid". Kalau ditambah effect system, kita bisa menyatakan "fungsi ini mengembalikan solusi Sudoku yang valid tetapi tidak mengakses jaringan maupun filesystem". Jika LLM berkembang lebih jauh, mungkin tingkat seperti ini pun bisa dicapai di Python, tetapi jika lajunya lambat, menurut saya masa depan adalah memanfaatkan LLM yang kurang andal dengan membungkusnya dalam sistem deterministik yang andal.
Saat memakai cats-effect di Scala, bantuan LLM benar-benar mempercepat pengembangan. Kode cats-effect sering membuat konsep yang sederhana terasa sulit, tetapi cukup bertanya ke LLM, "bagaimana cara melakukan ~ di cats-effect?", dan 80% masalah langsung terpecahkan. Sisa 20% bisa diatasi dengan memberi konteks tambahan. Dari sudut pandang maintainability masih saya uji, tetapi frustrasi dalam pemrograman fungsional berbasis effect berkurang drastis. Berikutnya saya ingin menguji seberapa baik Claude Code melakukannya.
Haskell punya dua keunggulan besar untuk code generation dengan LLM. Pertama, type system yang ekspresif menangkap banyak kesalahan sehingga compile error yang muncul bisa langsung diberikan kembali ke LLM sebagai feedback. Kedua, sangat mudah melakukan penyempurnaan kode yang efisien dan akurat lewat property-based testing (QuickCheck dan sejenisnya). LLM memang tidak terlalu pandai menulis test itu sendiri, tetapi kalau kita menambahkannya secara manual, bug pada kode hasil generasi pun cepat terdeteksi.
Tulisan ini membuat saya akhirnya menutup pertanyaan, "kenapa tidak pakai F# saja daripada OCaml?" Di hampir semua thread tentang OCaml, selalu muncul usulan, "kalau pakai F#, masalah tooling-nya selesai, kan?" Saya juga sempat penasaran dengan OCaml dan tertarik karena pernah melihat julukannya sebagai "Go with types", tetapi sejauh ini OCaml sendiri belum terasa sepenuhnya menarik. Rasanya berbeda dengan antusiasme yang saya lihat di komunitas bahasa lain seperti Erlang, Ruby, Rust, atau Zig.
Saya justru pindah ke OCaml untuk menghindari ekosistem tooling F#. Saat saya memakainya, compiler F# lambat, ekosistemnya terpusat ke C#, MSBuild lemah dan dokumentasinya minim, Ionide sering crash, dan Fantomas juga tidak andal. Namun OCaml pun tidak menggantikan semua fitur F# yang berorientasi performa (misalnya value type dan hal lain yang didukung CLR). Dalam arti itu, sampai sekarang saya masih belum menemukan bahasa keluarga ML yang sederhana. Saya berharap OxCaml atau yang lain bisa memecahkannya di masa depan.
Belakangan ini saya memang tidak banyak memakai OCaml, tetapi inti bahasanya sendiri tetap yang paling saya sukai. Gaya kode saya cenderung mengarah ke satu fungsi besar, dan di OCaml saya secara alami terdorong untuk menghindarinya. Saya memakai Rust untuk side project, tetapi sejujurnya OCaml terasa lebih nyaman. Karena alasan di atas, saya juga sangat ingin mencoba F#.
Saya punya pertanyaan soal istilah: di artikel, functional types disebut "exponential types". Saya kurang paham kenapa tipe fungsi tingkat tinggi disebut tipe eksponensial.
Sudah ada penjelasan yang bagus, tetapi alasan yang lebih dalam adalah karena tipe fungsi mengikuti hukum eksponen secara aljabar. Misalnya
A → (B → C)melalui currying isomorfik dengan(A × B) → C. Ini mirip dengan(cᵇ)ᵃ = cᵇ˙ᵃ. Lalu(A + B) → Cisomorfik dengan(A → C) × (B → C), yang sesuai dengan aturancᵃ⁺ᵇ = cᵃ·cᵇ.Bahkan tipe fungsi orde pertama pun sudah eksponensial. Misalnya, sum type memiliki sejumlah nilai sebanyak jumlah kasusnya. (Contoh:
A of bool | B of bool→2+2=4kemungkinan). Product type dan exponential type juga sama. Kalau ditulisbool -> bool, ada2^2 = 4kemungkinan nilai (kalau kita abaikan efek samping).Biasanya saat membahas ADT (Algebraic Data Type), orang hanya membahas sum dan product. Fungsi tidak sering disebut karena bukan data. Tetapi tipe
a -> bpunyab^akemungkinan, jadi bisa didekati dengan cara yang sama.Saya juga punya pertanyaan yang sama, tetapi secara matematis setelah penjumlahan (sum) dan perkalian (product), berikutnya memang eksponen, jadi mungkin istilah itu dipakai secara analogi.
Semua balasan di atas benar, tetapi sebenarnya dalam category theory, tipe fungsi disebut "exponential product". Nama itu juga berasal dari fakta bahwa jumlah fungsi dari A ke B dihitung sebagai kardinalitas B dipangkatkan dengan kardinalitas A.
Kasus dalam sum-type adalah nilai (expression) melalui type constructor, jadi tentu saja mereka bertipe. Misalnya,
Masing-masing kasus diberi tipe. Berkat pattern matching, parameter pada type constructor juga langsung bisa di-unpack. Kalau kasus-kasusnya dipisahkan menjadi tipe terpisah, keunggulan exhaustiveness (mencegah kasus terlewat) dari sum-type hilang, sehingga justru memungkinkan keadaan program yang salah untuk direpresentasikan. Sum-type dideklarasikan sekali lalu digunakan berkali-kali, dan biasanya bersifat disposable. Keterbacaan kode juga penting, jadi verbosity sering diremehkan. Sebagai catatan, C#/Java tidak benar-benar mendukung sum-type. Contoh di bawah menunjukkan bagaimana C# menjadi tidak perlu rumit karena pendekatan OOP-nya.
Di ML ini bisa jauh lebih ringkas:
Kedua pendekatan itu nyaris sama, tetapi unsur OOP di C# justru menjadi penghambat.
Di OCaml, kita juga bisa memakai GADT, polymorphic variants, dan sebagainya agar tiap kasus bisa dipakai seolah tipe terpisah. Tetapi secara umum, memisahkan sum-type membuat generalisasi lebih sulit dan pemahamannya juga lebih rumit. Masalah type equality dan variance pun ikut muncul.
Saya tidak begitu paham kenapa harus memperdebatkan sum-type versus sealed-type. Saya pribadi lebih menyukai bahasa fungsional, tetapi berkat kemampuan pembedaan di level tipe, sealed type saja sebenarnya cukup untuk memodelkan semua sum-type, dan berkat subtyping, definisi serta penggunaannya kadang justru lebih mudah. Paradigma sistemnya memang sangat berbeda, tetapi secara matematis hampir setara, dan berbagai trik tipe di OOP maupun FP pada dasarnya bisa diimplementasikan selama bahasa itu mengizinkannya.
Saya tidak setuju bahwa deklarasi sum-type di Java/Kotlin cukup berharga untuk menebus verbosity-nya. Itu terasa seperti boilerplate khas bahasa JVM.
Akan bagus kalau ada orang yang benar-benar paham sintaks ReasonML sampai tingkat ini untuk membandingkan kelebihan dan kekurangannya. (Di artikel memang hanya disinggung sebentar.)
Hal yang paling saya sayangkan adalah let binding (dokumentasi resmi). Di ReasonML, operator seperti
>>=untuk monad bisa dikustomisasi sendiri sehingga enak dipakai. rescript (fork dari ReasonML) masih belum punya itu. Sebagai gantinya, ia mendukung sintaks async/await dengan baik sehingga membantu untuk kode asinkron. Melange (yang sempat disebut di artikel) mendukung let binding dalam sintaks Reason. Karena itu, untuk frontend berbasis React, Reason ML di Melange sangat menguntungkan. Berkat let binding (bersama JSX), kode asinkron bergaya monadik juga bisa ditulis dengan rapi. Dalam sintaks OCaml, ini memang bisa diakali lewat PPX, tetapi highlight di editor sering tidak berjalan baik. Dari sisi backend, saya suka gaya Python, jadi kurung kurawal masih terasa mengganggu, dan saya suka pemanggilan atau definisi fungsi tanpa tanda kurung. Namun sebagai pengguna OCaml pemula, saya masih merasa sulit karena tanda kurung pada argumen non-variabel sering membingungkan. Semoga pengalaman ini membantu.Saya hampir tidak pernah memakai ReasonML, jadi saya tidak benar-benar merasakan kelebihannya. Selain fakta bahwa ia sempat mati dua kali selama 4 tahun...
Saya berharap sintaks Reason lebih tersebar luas, tetapi kalau ingin berkomunikasi dengan komunitas OCaml, lebih baik langsung belajar sintaks standar. Kebanyakan kode dan dokumentasi memakai sintaks standar, jadi pada akhirnya tetap harus paham juga.
Hal paling tidak nyaman dari ReasonML dalam pengalaman saya adalah LSP-nya tidak bekerja dengan baik.
Saya ingin penjelasan lebih rinci tentang bagian yang mengimplementasikan dependency injection dengan effects system. Gagasan mengikat nilai untuk test dan production lewat pattern matching terdengar menarik, tetapi dari tulisan saja saya belum benar-benar menangkapnya. Saya juga baru tahu bahwa module system punya type system tersendiri, dan itu menarik.