Belajar Haskell - cabal & Build Tooling
Episode 9 of 23

Belajar Haskell - cabal & Build Tooling

Mengelola proyek Haskell secara profesional dengan cabal: struktur cabal.project dan file .cabal berisi exposed-modules, dependencies dan GHC options, alur build test run, snapshot reproducible dengan stack dan resolver Stackage LTS, serta setup HLS yang konsisten dengan versi GHC.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sampai episode 8 kalian menjalankan kode dengan runghc dan GHCi. Di episode 9 ini kita naik level: mengelola proyek Haskell yang sebenarnya — dengan dependensi, banyak modul, dan build yang reproducible. Ini adalah titik di mana kode "lab" berubah menjadi kode "produksi".

Mengapa bab ini penting? Karena hampir semua proyek Haskell di dunia nyata dibangun dengan cabal atau stack. Memahami file .cabal, cabal.project, dan snapshot stack berarti kalian bisa berkontribusi ke proyek open source, membangun aplikasi dengan dependensi puluhan paket, dan mengatasi error build yang selama ini terasa seperti kotak hitam.

Anatomi Proyek cabal

Proyek cabal terdiri dari dua file kunci: cabal.project (konfigurasi level proyek) dan file *.cabal (deskripsi paket).

File .cabal

Ini adalah "peta" sebuah paket. Contoh minimal:

hello-lab.cabal
cabal-version:      3.0
name:               hello-lab
version:            0.1.0.0
 
executable hello-lab
  main-is:          Main.hs
  build-depends:    base >= 4.17 && < 5
  hs-source-dirs:   app
  default-language: Haskell2010

Bagian-bagian pentingnya:

  • executable hello-lab mendefinisikan binary bernama hello-lab.
  • main-is: Main.hs menunjuk file dengan fungsi main.
  • build-depends: base >= 4.17 mendeklarasikan dependensi — di sini base (yang berisi Prelude). Operator && menghubungkan batas bawah dan atas.
  • hs-source-dirs: app lokasi file sumber.

Menambahkan Modul Library

Untuk paket yang lebih besar, definisikan komponen library yang mengekspos modul agar bisa di-import oleh executable dan test:

Paket dengan library
library
  exposed-modules:  MathOps
  build-depends:    base >= 4.17 && < 5
  hs-source-dirs:   src
 
executable hello-lab
  main-is:          Main.hs
  build-depends:    base, hello-lab
  hs-source-dirs:   app
  default-language: Haskell2010

exposed-modules adalah daftar modul yang bisa di-import pihak lain. Perhatikan build-depends: base, hello-lab di executable — executable memakai library dari paket yang sama. Struktur inilah yang dipakai oleh hampir semua proyek Haskell modern: kode di src/, executable tipis di app/, test di test/.

cabal.project

cabal.project berada di akar proyek dan mengatur konfigurasi level paket — memilih paket yang ikut dibangun, opsi GHC global, dan sumber tambahan:

cabal.project
packages: .
tests: True
optimization: 2
 
package *
  ghc-options: -Wall -Werror

packages: . berarti "bangun paket di direktori ini", tests: True mengaktifkan komponen test, dan blok package * menerapkan ghc-options ke semua paket. -Wall -Werror menjadikan semua peringatan sebagai error — praktik ketat yang akan kita bahas di episode 17.

Alur Build: build, test, run

Alur utama cabal
cabal build            # kompilasi semua komponen
cabal test             # jalankan test suite
cabal run hello-lab    # jalankan executable tertentu
cabal repl             # GHCi dengan konteks paket
cabal list --installed # lihat paket terinstall

cabal repl sangat berguna: membuka GHCi dengan semua dependensi paket kalian ter-load, jadi bisa mencoba fungsi dari src/ langsung.

Stack: Snapshot Reproducible

stack membungkus cabal dan menambahkan snapshot: kumpulan versi paket yang sudah diuji kompatibel (dari Stackage). Daripada kalian menentukan versi tiap dependensi, stack menentukan satu resolver LTS dan menurunkannya untuk semua paket.

stack.yaml
resolver: lts-23.5
packages:
  - .

resolver: lts-23.5 memilih snapshot LTS tertentu. Selama resolver tetap sama, setiap developer dan CI akan menginstall versi persis yang sama — inilah reproduktifitas. Perintahnya nyaris identik dengan cabal:

Alur utama stack
stack build
stack test
stack run hello-lab
stack ghci

Kapan Memilih cabal atau stack

KebutuhanPilihan
Kontrol penuh versi dependensicabal
Reproduktifitas tinggi untuk tim/CIstack (resolver tetap)
Menulis library untuk Hackagecabal
Belajar cepat tanpa mikir versistack
Proyek lama yang sudah pakai salah satuikuti yang ada

Keduanya berbagi format file .cabal — jadi pindah dari stack ke cabal (atau sebaliknya) tidak mengharuskan menulis ulang deskripsi paket.

Tip

Tips praktis: jika proyek sudah punya stack.yaml, gunakan stack; jika hanya cabal.project, gunakan cabal. Menjalankan keduanya di proyek yang sama jarang menjadi masalah (file .cabal sama), tetapi jangan sampai memakai dua lockfile yang saling menimpa build cache — pilih satu sebagai primary.

Setup HLS yang Benar

Haskell Language Server butuh versi GHC yang sama dengan proyek kalian. Langkah standar:

Pastikan HLS cocok dengan GHC
ghcup set ghc 9.14.1
ghcup install hls recommended
ghcup set hls recommended

Jika proyek memakai GHC berbeda (lewat stack.yaml), stack mengelola HLS-nya sendiri dengan stack install hls dan stack --version otomatis. Verifikasi di VS Code: buka file .hs, pastikan tipe muncul saat hover dan tidak ada pesan "HLS not found".

cabal repl vs GHCi Biasa

Perbedaan penting: cabal repl dan stack ghci memuat konteks proyek — dependensi dan modul kalian siap pakai. GHCi biasa hanya tahu Prelude. Selama development, selalu gunakan versi yang sesuai toolchain proyek kalian.

Warning

Error build "cannot satisfy -package-id" atau "unexpected GHC version" hampir selalu karena dua GHC berbeda dipakai oleh toolchain dan HLS. Periksa dengan ghc --version dan ghcup list; pastikan versi yang aktif (tagged) sesuai dengan yang dipakai proyek.

Kesalahan Umum (Common Pitfalls)

  1. File .cabal dan cabal.project di direktori salah.cabal berada di folder paket, cabal.project di akar proyek.
  2. Lupa mendaftarkan modul di exposed-modulesimport MyModule gagal walaupun file-nya ada.
  3. Batas base terlalu ketatbase >= 4.17 && < 4.18 akan pecah saat GHC di-upgrade. Gunakan < 5 sebagai batas atas agar masa pakai lebih lama.
  4. -Wall tidak diaktifkan — di episode 17 kalian akan lihat betapa banyak bug yang dicegah hanya oleh warning ini.

Penutup

Inti yang harus dibawa pulang:

  • Proyek cabal = file *.cabal (deskripsi paket) + cabal.project (konfigurasi level proyek).
  • exposed-modules mendaftarkan API library; build-depends mendeklarasikan dependensi.
  • Alur: cabal build, cabal test, cabal run, cabal repl.
  • stack = cabal + resolver Stackage (LTS) untuk build reproducible.
  • HLS harus seversi dengan GHC proyek; ghcup set untuk menyamakannya.

Di episode 10 selanjutnya kita akan membahas testing (HUnit/Hspec/QuickCheck) & debug — menulis unit test dengan HUnit dan spesifikasi BDD dengan Hspec, property-based testing dengan QuickCheck, menghitung coverage dengan hpc, serta debugging dengan :t/:i, Debug.Trace, dan :set +s. Sampai jumpa di episode 10!