Program yang terus bertumbuh tidak lagi muat dalam satu berkas, baik dari sisi panjang maupun dari sisi kejelasan. Sistem modul Haskell memecah program menjadi satuan yang saling diimpor, dan setiap modul menentukan sendiri bagian mana yang boleh digunakan modul lain. Topik ini membahas deklarasi modul beserta daftar ekspornya, cara mengimpor, serta nama qualified yang menjaga pemanggilan tetap jelas ketika sumbernya bertambah.
Deklarasi modul dan daftar ekspor#
Deklarasi module pada baris pertama berkas menamai modulnya, dan daftar di dalam tanda kurung menyatakan apa saja yang diekspor. Modul berikut mengekspor dua fungsi lingkaran, sedangkan fungsi bantu piLokal tidak masuk daftar sehingga menjadi detail internal yang tidak dapat dipanggil dari luar.
-- Modul geometri lingkaran: fungsi bantu tidak diekspor
module Geometri (luasLingkaran, kelilingLingkaran) where
piLokal :: Double
piLokal = 3.14159
luasLingkaran :: Double -> Double
luasLingkaran r = piLokal * r * r
kelilingLingkaran :: Double -> Double
kelilingLingkaran r = 2 * piLokal * r
main :: IO ()
main = do
print (luasLingkaran 7)
print (kelilingLingkaran 7)153.93791
43.98226Nama modul diawali huruf kapital dan mengikuti nama berkasnya, yaitu Geometri.hs untuk modul Geometri, kebiasaan yang membuat pencarian berkasnya dapat ditebak. Daftar ekspor berperan sebagai antarmuka, karena piLokal tetap digunakan di dalam modul tetapi tidak terlihat dari luar, sehingga nilai konstanta itu bebas diganti tanpa mengganggu pengguna lain. Apabila seluruh nama diekspor sebagaimana adanya, daftar diganti module Geometri where, bentuk yang praktis untuk modul internal kecil.
Antarmuka juga menyertakan tipe yang didefinisikan modulnya, dan bentuk module Geometri (Bentuk (..), luasLingkaran) mengekspor tipe Bentuk beserta seluruh konstruktornya sekaligus fungsinya. Tanda (..) itu dapat dipersempit menjadi daftar konstruktor tertentu, sehingga modul tetap mengendalikan nilai mana yang boleh dibangun langsung oleh pengguna. Kontrol semacam ini bekerja berdampingan dengan pola smart constructor untuk menjaga tipe tervalidasi tetap sah di seluruh pemakaiannya.
Impor dari modul lain#
Modul yang membutuhkan fungsi tersebut mengimpornya dengan import Geometri, dan daftar pada impor menyaring lagi apa yang diterima. Berkas berikut hanya mengambil luasLingkaran dan menulis pemanggilannya secara qualified, yaitu mengawali setiap nama dengan nama modulnya.
-- tidak-dijalankan
-- Pemakaian modul Geometri dari berkas lain
import qualified Geometri as G
main :: IO ()
main = print (G.luasLingkaran 10)Berkas itu berada di samping Geometri.hs, sehingga eksekusinya dilakukan dari direktori yang memuat keduanya agar modulnya ditemukan.
# jalankan program pemakai dari direktori yang memuat Geometri.hs
runghc pakai-geometri.hs314.159Kata kunci qualified membuat seluruh nama modul wajib diawali G., dan kata as G menyingkat penulisannya. Bentuk ini membuat asal setiap pemanggilan terbaca langsung pada tempat pemakaiannya, kebiasaan yang membantu ketika satu program mengimpor banyak modul. Tanpa qualified, pemanggil cukup menulis luasLingkaran 10 sebagaimana ditulis di dalam modul asalnya, tetapi asal fungsi tersebut baru diketahui setelah daftar impornya diperiksa.
Kebiasaan penulisan juga menjaga bagian impor tetap terbaca, yaitu impor dikelompokkan mulai dari pustaka standar, pustaka pihak ketiga, lalu modul proyek sendiri, dengan satu baris kosong di antara kelompoknya. Susunan semacam ini tidak diwajibkan compiler, tetapi membuat ketergantungan berkas terbaca sekilas dan memudahkan penilaian apakah sebuah perubahan menambah dependensi baru. Daftar impor yang rapi juga menjadi tempat pemeriksaan pertama ketika konflik nama dilaporkan.
Nama qualified pada pustaka standar#
Kebiasaan yang sama berlaku untuk pustaka standar, dan modul Data.Char menyediakan contoh yang mudah dicoba. Perintah berikut memanggil toUpper melalui nama panjangnya tanpa menulis impor apa pun, karena nama qualified sah digunakan kapan pun.
# panggil fungsi pustaka standar melalui nama qualified-nya
ghc -e "Data.Char.toUpper 'h'"'H'Pemanggilan Data.Char.toUpper menunjukkan aturan umumnya, yaitu setiap nama modul boleh digunakan sebagai awalan tanpa perlu mengimpor terlebih dahulu pada ekspresi tunggal. Pada berkas yang sesungguhnya, impor tetap ditulis di bagian atas, dan pilihan berikutnya berupa mengimpor dengan daftar eksplisit seperti import Data.Char (toUpper) untuk membatasi nama yang masuk, atau import qualified Data.Map as M untuk pustaka yang konvensinya memang dipanggil melalui singkatan. Ketiga bentuk itu dapat digabung pada satu berkas sesuai kebutuhan masing-masing modulnya.
Pilihan bentuk impor memengaruhi keterbacaan pemanggilan di seluruh berkas. Impor dengan daftar eksplisit membuat nama yang benar-benar digunakan terlihat pada bagian atas berkas, sedangkan pemanggilan qualified membuat sumbernya terlihat pada setiap baris pemanggilan. Keduanya mendukung kejelasan dari arah yang berbeda, dan kebiasaan yang umum berupa menggabungkan keduanya, yaitu impor eksplisit untuk fungsi yang dipanggil padat, serta qualified untuk modul yang luas seperti Data.Map.
Kesalahan umum#
Kesalahan pertama adalah mengimpor dua modul yang menyediakan nama sama untuk dua entitas yang berbeda, sehingga setiap penggunaan nama itu ditolak compiler karena tidak jelas mana yang dimaksud. Perbaikannya berupa salah satu dari dua cara, yaitu mengimpor dengan daftar eksplisit sehingga hanya satu sumber yang masuk, atau menggunakan nama qualified untuk memanggil sumber yang dituju secara langsung.
Kesalahan kedua berupa mengekspor semuanya melalui module M where pada modul yang seharusnya memiliki antarmuka, sehingga fungsi bantu ikut terlihat dan pengguna lain mulai bergantung padanya. Setiap ketergantungan pada fungsi internal membuat perubahan internal menjadi perubahan yang memecah pengguna, sedangkan daftar ekspor yang dijaga tetap menarik garis batas antara antarmuka yang stabil dan detail yang bebas berubah.
Ringkasan#
- Daftar ekspor pada
modulemenetapkan antarmuka modul, dan nama yang tidak diekspor tetap internal meskipun digunakan di dalamnya. importdengan daftar eksplisit menyaring nama yang masuk, sedangkanqualifiedbesertaasmewajibkan awalan yang membuat asal pemanggilan terbaca.- Nama qualified seperti
Data.Char.toUppersah digunakan tanpa impor pada ekspresi tunggal, dan konvensinya berlaku sama untuk pustaka standar maupun modul proyek. - Konflik nama antardua entitas diselesaikan dengan daftar impor eksplisit atau pemanggilan qualified, bukan dengan menghapus salah satu impor.
Referensi#
- GHC Users Guide: Separate compilation — haskell.org — Panduan resmi sistem modul GHC, termasuk pencarian berkas modul.
- Haskell 2010 Language Report, Bab 5 — haskell.org — Spesifikasi deklarasi modul, ekspor, dan impor pada bahasa.
- Data.Char — Hackage — Dokumentasi modul yang menjadi contoh pemanggilan qualified.