Tipe data aljabar (algebraic data type) adalah bentuk utama deklarasi tipe di Haskell, yaitu tipe yang tersusun dari beberapa alternatif konstruktor dengan sejumlah field, yaitu bagian data pada sebuah konstruktor. Topik ini membahas sintaksis pernyataan data, perbedaan tipe sum dan tipe product, serta peran konstruktor sebagai nilai dan sebagai fungsi. Setelah membacanya, tipe sesuai ranah masalah dapat dideklarasikan dari nol dan setiap pesan error compiler pada deklarasi data dapat diuraikan dengan langkah yang jelas.
Anatomi deklarasi data#
Deklarasi data dimulai dari kata kunci data, dilanjutkan nama tipe, tanda =, kemudian konstruktor data (data constructor) yang dipisahkan tanda |. Tipe yang digunakan sepanjang topik ini adalah Bentuk dengan tiga alternatif konstruktor, yaitu Lingkaran, PersegiPanjang, dan SegitigaSamaSisi. Nama tipe dan nama konstruktor sama-sama wajib diawali huruf kapital, sehingga keduanya mudah dibedakan dari nama fungsi dan nama variabel yang selalu diawali huruf kecil. Aturan ini membuat baris seperti Lingkaran 3 segera dikenali sebagai pembentukan nilai, bukan pemanggilan fungsi biasa.
Jumlah field pada sebuah konstruktor menentukan bentuk konstruktor tersebut, sebagaimana dirangkum tabel berikut.
| Konstruktor | Jumlah field | Bentuknya |
|---|---|---|
Kosong | 0 | nilai konstanta |
Lingkaran Double | 1 | fungsi satu argumen |
PersegiPanjang Double Double | 2 | fungsi dua argumen |
Konstruktor tanpa field berdiri sendiri sebagai nilai jadi, sedangkan konstruktor dengan field berperilaku persis seperti fungsi. Konstruktor Lingkaran bertipe Double -> Bentuk, sehingga konstruktor dapat dipetakan dan diterapkan secara parsial sebagaimana fungsi lain. Sifat ini terlihat pada program di bagian berikutnya melalui ekspresi map Lingkaran.
Tipe sum#
Tipe sum (sum type) adalah tipe yang nilainya merupakan salah satu dari beberapa alternatif. Tanda | pada deklarasi dibaca sebagai “atau”, sehingga nilai Bentuk adalah sebuah Lingkaran, atau sebuah PersegiPanjang, atau sebuah SegitigaSamaSisi, tetapi tidak pernah lebih dari satu sekaligus. Bentuk paling sederhananya adalah tipe dengan konstruktor tanpa field, seperti Bool bawaan yang dideklarasikan dengan dua alternatif, yaitu False dan True. Program berikut menghitung luas untuk ketiga alternatif Bentuk.
-- Tipe sum sekaligus product: satu tipe, tiga alternatif konstruktor
data Bentuk
= Lingkaran Double -- satu field: jari-jari
| PersegiPanjang Double Double -- dua field: lebar dan tinggi
| SegitigaSamaSisi Double -- satu field: panjang sisi
deriving (Show, Eq)
luas :: Bentuk -> Double
luas bentuk = case bentuk of
Lingkaran r -> pi * r * r
PersegiPanjang l t -> l * t
SegitigaSamaSisi s -> s * s * sqrt 3 / 2
main :: IO ()
main = do
print (luas (Lingkaran 3))
print (luas (PersegiPanjang 4 5))
print (luas (SegitigaSamaSisi 2))
print (map Lingkaran [1, 2, 3])28.274333882308138
20.0
3.4641016151377544
[Lingkaran 1.0,Lingkaran 2.0,Lingkaran 3.0]Fungsi luas menangani ketiga alternatif melalui pencocokan pola (pattern matching) pada konstruktor, sehingga setiap cabang perhitungan terdefinisi dengan jelas. Baris terakhir keluaran menunjukkan sifat konstruktor sebagai fungsi, yaitu map Lingkaran [1, 2, 3] menghasilkan tiga nilai Bentuk dalam satu pemetaan. Klausa deriving (Show, Eq) menghasilkan instans Show dan Eq secara otomatis agar nilai dapat dicetak dengan print dan dibandingkan dengan ==.
Tipe product#
Tipe product (product type) adalah tipe yang nilainya menggabungkan beberapa bagian sekaligus. Konstruktor PersegiPanjang menerima dua field, yaitu lebar dan tinggi, dan kedua field wajib ada karena persegi panjang tanpa salah satunya tidak bermakna. Apabila tipe sum dibaca sebagai “atau”, tipe product dibaca sebagai “dan”, sehingga gabungan keduanya menutupi hampir seluruh kebutuhan perancangan tipe.
Struktur bawaan yang paling dekat dengan tipe product adalah tuple. Perintah berikut membentuk satu nilai tuple yang menggabungkan sebuah bilangan dan sebuah teks, kemudian mencetaknya apa adanya.
# tuple bawaan adalah tipe product: dua bagian dalam satu nilai
ghc -e '(12, "dua belas")'(12,"dua belas")Keluaran menampilkan kedua bagian sekaligus karena tuple menyimpan keduanya sebagai satu kesatuan. Tuple merupakan product generik yang bagian-bagiannya tanpa nama, sehingga isinya hanya dapat diambil melalui posisi seperti fst dan snd. Deklarasi data dengan field bernama adalah perluasan ide ini untuk ranah masalah nyata, dan bentuknya dibahas pada topik record.
Membaca deklarasi secara aljabar#
Pemilihan kata sum dan product berasal dari cara membaca deklarasi sebagai ekspresi. Tipe sum menjumlahkan kemungkinan, karena tiga alternatif konstruktor pada Bentuk memberi tiga jalur pembentukan nilai yang berdampingan. Tipe product mengalikan bagian, karena PersegiPanjang yang memuat dua field menghasilkan nilai dari gabungan keduanya. Deklarasi yang memuat keduanya, seperti Bentuk, sering disebut tipe sum dari tipe product (sum of products).
Pembacaan aljabar ini bukan sekadar penamaan, melainkan panduan praktis saat merancang tipe. Keadaan yang merupakan pilihan di antara alternatif dinyatakan sebagai sum, sedangkan keadaan yang merupakan gabungan bagian dinyatakan sebagai product. Keputusan itu langsung menentukan letak tanda | dan jumlah field pada setiap konstruktor. Tipe bawaan juga dapat dibaca dengan cara yang sama, misalnya Bool sebagai sum dua alternatif dan tuple dua unsur sebagai product dua tipe.
Kesalahan umum#
- Nama konstruktor diawali huruf kecil. Deklarasi seperti
data bentuk = lingkaran Doubleditolak compiler dengan pesan error sintaks, karena pengenal berawalan huruf kecil dicadangkan untuk fungsi dan variabel. Perbaikannya adalah menuliskan ulang nama tipe dan nama konstruktor dengan huruf kapital di awal. - Nama konstruktor digunakan sebagai nama tipe. Anotasi seperti
x :: Lingkaranditolak compiler karenaLingkaranadalah konstruktor data yang berada di ruang nilai, bukan nama tipe. Anotasi yang benar menggunakan nama tipe, yaitux :: Bentuk. - Konstruktor dipanggil tanpa field yang wajib. Ekspresi
Lingkarantanpa argumen bukanlah nilaiBentukyang sudah terisi, melainkan fungsi yang masih menunggu satuDouble, sehingga diteruskan keprintakan memunculkan pesan error tipe. Apabila sebuah nilai utuh dibutuhkan, seluruh field konstruktor harus diisi, misalnyaLingkaran 3. - Alternatif yang terlewat pada pencocokan pola. Persamaan
luasyang hanya menangani dua dari tiga konstruktor tidak selalu ditolak saat kompilasi, tetapi program dapat berhenti dengan error saat berjalan apabila alternatif yang terlewat benar-benar diterima. Peringatan pencocokan pola tidak lengkap muncul jika kompilasi dijalankan dengan bendera-Wall, sehingga bendera tersebut layak diaktifkan.
Ringkasan#
- Deklarasi
datamemperkenalkan nama tipe beserta konstruktor datanya, dan keduanya diawali huruf kapital. - Konstruktor tanpa field adalah nilai konstanta, sedangkan konstruktor dengan field berperilaku seperti fungsi biasa, sehingga dapat dipetakan dan diterapkan secara parsial.
- Tipe sum menyatakan pilihan di antara alternatif dengan tanda
|, sedangkan tipe product menyatukan beberapa field dalam satu konstruktor. - Fungsi di atas tipe aljabar menangani setiap alternatif melalui pencocokan pola pada konstruktor.
Referensi#
- Haskell 2010 Report, bab 4: Declarations and bindings — haskell.org — Bagian deklarasi pada laporan bahasa resmi yang memuat bentuk deklarasi tipe data aljabar.
- Algebraic data type — HaskellWiki — Penjelasan komunitas tentang tipe sum dan product beserta contoh deklarasinya.