Menjembatani Haskell dengan dunia luar: Foreign Function Interface ke C dengan foreign import ccall dan pemahaman ABI, serta kompilasi ke WebAssembly lewat target wasm32wasi yang stabil di GHC 9.14 untuk menjalankan logika Haskell di browser dan edge runtime bersama interop JavaScript.

Tidak ada bahasa yang hidup sendiri. Di episode 18 ini kita membuka pintu antara Haskell dan ekosistem di luar dirinya: C/C++ lewat Foreign Function Interface (FFI), dan WebAssembly untuk menjalankan kode Haskell di tempat yang tidak pernah terpikirkan sebelumnya — browser dan edge runtime.
Mengapa bab ini penting? Karena di dunia nyata, kalian akan memanggil library C yang sudah teruji (kripto, compression, native driver), atau diminta memakai logika Haskell dari aplikasi lain. FFI adalah jembatan itu, dan dukungan WASM yang stabil di GHC 9.14 membuka peluang distribusi yang benar-benar baru.
Foreign Function Interface (FFI) memungkinkan Haskell memanggil fungsi dari bahasa lain — praktis selalu C. Sintaks dasarnya foreign import:
foreign import ccall unsafe "math.h pow"
c_pow :: Double -> Double -> Double
main :: IO ()
main = print (c_pow 2.0 10.0) -- 1024.0foreign import — mendeklarasikan pemanggilan keluar dari Haskell.ccall — calling convention standar C."math.h pow" — header dan nama simbol di C.c_pow — nama fungsi di sisi Haskell.Kalau nama fungsi Haskell sama dengan simbol C, string bisa dihilangkan:
foreign import ccall unsafe "sqrt"
sqrtC :: Double -> DoubleProses dua arah: tulis kode C, link dengan binary Haskell, lalu panggil.
int adder(int a, int b) {
return a + b;
}foreign import ccall unsafe "adder"
c_adder :: Int -> Int -> Int
main :: IO ()
main = print (c_adder 40 2) -- 42Kompilasi dengan GHC, menyertakan file C:
ghc -O2 Main.hs csrc/adder.c -o app
./appGHC mengkompilasi .c secara otomatis — tanpa makefile khusus untuk kasus sederhana.
ABI (Application Binary Interface) adalah "kontrak di level binary": bagaimana fungsi dipanggil, bagaimana argumen dan nilai kembali dilewatkan, dan bagaimana memori diatur. Ketika FFI menyebut "calling convention", itulah bagian dari ABI. Poin-poin penting:
Int di Haskell dipetakan ke long/intptr_t C — ukuran platform-dependent. Gunakan tipe eksplisit (CInt, CLong, CString, CUChar) dari Foreign.C.Types untuk mencegah kebingungan ukuran.String Haskell tidak bisa langsung dipakai; konversi lewat CString (peka nul-terminated) dengan helper newCString/peekCString.Warning
Kesalahan ABI paling umum: memetakan Int Haskell ke int C. Int Haskell adalah 64-bit (bergantung platform), sedangkan int C bisa 32-bit. Untuk interoperabilitas yang aman, selalu gunakan tipe dari Foreign.C.Types — CInt, CString, dan kawan-kawan — supaya ukurannya jelas dan dijamin sama di kedua sisi.
FFI menembus semua jaminan tipe — kalian perlu disiplin ekstra:
unsafe vs safe: foreign import ... unsafe mengasumsikan fungsi C cepat dan tidak memanggil kembali ke Haskell; untuk I/O yang lama atau blocking, gunakan safe agar tidak memblokir seluruh runtime.IO atau monad khusus.NULL; cek dan terjemahkan ke Maybe/Either di sisi Haskell — jangan pernah membiarkan pointer mentah bocor ke logika.FFI juga bekerja sebaliknya — mengekspor fungsi Haskell sebagai API C yang bisa dipanggil bahasa lain. Ini cara mengemas logika Haskell menjadi library yang bisa dipakai Python, Rust, atau C:
foreign export ccall "haskell_double"
haskell_double :: Int -> Int
haskell_double :: Int -> Int
haskell_double x = x * 2Setelah dikompilasi, haskell_double menjadi simbol C yang bisa di-extern dari bahasa lain. Teknik ini dipakai untuk membungkus logika critical (misal mesin bisnis) menjadi service yang bahasa lain hanya panggil — menjaga correctness logic tetap di Haskell.
WebAssembly (WASM) adalah target eksekusi universal: binary yang berjalan di browser, edge runtime, dan container ringan. Sejak GHC 9.14, target wasm32wasi stabil — artinya kalian bisa mengkompilasi Haskell menjadi WASM dengan toolchain standar.
ghcup install wasm
ghc --target=wasm32wasi -O2 Main.hs -o app.wasmBeberapa hal yang perlu diketahui:
IO perlu menyesuaikan dengan kemampuan WASI.Untuk frontend, ekosistem Reflex (dengan Reflex-DOM) dan Jsaddle menyediakan cara membangun UI reaktif di browser sambil logika inti tetap di Haskell:
Dengan WASM stabil, jalur ini menjadi lebih praktis: logika Haskell yang sudah diuji dengan QuickCheck bisa dipakai di frontend tanpa ditulis ulang.
Tip
Pola arsitektur yang direkomendasikan: simpan semua logika domain dalam modul pure (tanpa IO), ekspor boundary yang tipis — via FFI ke C, atau via WASM ke JS. Kode pure berpindah platform secara gratis; kode yang menggantung IO harus ditulis ulang per platform. Dari sinilah muncul pepatah "keep the pure core large, the IO boundary thin".
CInt/CString/CUChar dari Foreign.C.Types, bukan Int/String.unsafe untuk pemanggilan yang blocking — lama-lama seluruh runtime terkunci; gunakan safe.malloc/newCString harus punya free/peekCString yang berpasangan.IO.IO besar.Inti yang harus dibawa pulang:
foreign import ccall) memanggil fungsi C; tipe harus memakai Foreign.C.Types.foreign export membalik arah — Haskell menjadi library C untuk bahasa lain.Di episode 19 selanjutnya kita akan membahas testing lanjut & property-based — custom Arbitrary dan shrinking di QuickCheck, stateful testing dengan Hedgehog, golden files untuk regression, serta mengintegrasikan semuanya ke CI dengan GitHub Actions dan GHC matrix. Sampai jumpa di episode 19!