Tipe yang mengikuti sebuah type class membutuhkan instans, yaitu implementasi bagi seluruh fungsi class tersebut. Haskell menyediakan dua cara untuk memperolehnya, yaitu menuliskan instans secara manual atau meminta compiler menghasilkannya dengan deriving. Topik ini membahas kedua cara tersebut, urutan yang mendasari hasil deriving, serta kesalahan yang lazim terjadi saat instans ditulis.
Instans yang dihasilkan dengan deriving#
Deklarasi data membentuk tipe baru dengan beberapa konstruktor nilai yang dipisahkan tanda garis vertikal, misalnya Dingin, Sedang, dan Panas untuk tipe Suhu. Klausa deriving pada akhir deklarasi meminta compiler menghasilkan instans untuk kumpulan class tertentu, yaitu Eq, Ord, Enum, Bounded, Show, dan Read. Cara ini menghilangkan penulisan berulang untuk perilaku yang polanya selalu sama, dan pembahasan lengkap deklarasi data berada di luar cakupan bagian ini.
-- Deriving: compiler menghasilkan instans Eq, Ord, Enum, dan Show
data Suhu = Dingin | Sedang | Panas
deriving (Eq, Ord, Enum, Show)
main :: IO ()
main = do
print (Dingin == Dingin)
print (Dingin == Panas)
print (Dingin < Panas)
print (max Dingin Panas)
print (fromEnum Sedang)
print [Dingin .. Panas]True
False
True
Panas
1
[Dingin,Sedang,Panas]Instans Eq yang dihasilkan membandingkan dua nilai secara struktural, yaitu menganggap keduanya sama jika konstruktor dan seluruh isinya sama, sehingga Dingin == Dingin bernilai True sementara Dingin == Panas bernilai False. Instans Ord mengurutkan nilai menurut urutan penulisan konstruktor, sehingga Dingin < Panas bernilai True dan max Dingin Panas menghasilkan Panas. Instans Enum memberi nomor pada konstruktor mulai dari nol, sehingga fromEnum Sedang menghasilkan 1, sedangkan instans Show menampilkan nama konstruktor, dan rentang [Dingin .. Panas] menyusun ketiga nilai sesuai urutannya.
Urutan deklarasi menentukan hasil#
Hasil deriving untuk Ord mengikuti urutan penulisan konstruktor pada deklarasi. Konstruktor yang ditulis lebih dahulu dianggap lebih kecil, sehingga deklarasi data Suhu = Dingin | Sedang | Panas menetapkan urutan Dingin, Sedang, Panas dari terkecil ke terbesar. Urutan yang sama menjadi dasar fromEnum, sehingga Ord dan Enum yang dihasilkan selalu konsisten satu sama lain.
Ketentuan serupa berlaku untuk class lain. Instans Eq yang dihasilkan menuntut seluruh field memiliki instans Eq, dan instans Show menuntut seluruh field memiliki instans Show. Apabila salah satu syaratnya tidak terpenuhi, compiler menolak deklarasi deriving dan menampilkan pesan error yang menyebut class yang kurang.
Deriving juga menjaga konsistensi antarclass, karena seluruh instans yang dihasilkan mengikuti satu sumber yang sama, yaitu deklarasi tipenya. Dua hasil deriving untuk tipe yang sama tidak mungkin saling bertentangan, sedangkan jaminan itu tidak ada apabila kedua instansnya ditulis manual oleh penulis yang berbeda.
Menulis instans manual#
Instans manual dipilih ketika makna perbandingan tidak mengikuti urutan penulisan konstruktor. Program berikut mendeklarasikan tipe Prioritas dengan konstruktor yang ditulis mulai dari yang paling mendesak, sedangkan urutan yang diinginkan ditetapkan melalui fungsi bobot. Instans Ord mendelegasikan perbandingan ke bilangan bobot, sehingga urutan deklarasi tidak lagi berpengaruh.
-- Konstruktor dideklarasikan menurut urutan penulisan, bukan urutan penting
data Prioritas = Tinggi | Normal | Rendah
deriving (Eq, Show)
-- Instans manual: urutan penting ditetapkan melalui bobot
bobot :: Prioritas -> Int
bobot Tinggi = 3
bobot Normal = 2
bobot Rendah = 1
instance Ord Prioritas where
compare a b = compare (bobot a) (bobot b)
main :: IO ()
main = do
print (Tinggi > Normal)
print (compare Rendah Normal)
print (max Normal Tinggi)
print (min Normal Rendah)True
LT
Tinggi
RendahPemanggilan Tinggi > Normal bernilai True meskipun Tinggi ditulis lebih dahulu pada deklarasi, karena bobot Tinggi adalah 3 sementara bobot Normal adalah 2. Fungsi compare Rendah Normal menghasilkan LT, dan fungsi min Normal Rendah menghasilkan Rendah, keduanya sejalan dengan bobot yang didefinisikan. Instans manual juga menjadi tempat menuliskan aturan khusus, misalnya perbandingan yang mengabaikan sebagian field.
Baris deriving (Eq, Show) pada Prioritas sengaja tidak memuat Ord, karena perbandingannya ditangani instans manual. Pemilihan ini memperlihatkan bahwa kedua cara tersebut dapat berdampingan pada satu tipe, yaitu class yang polanya standar dihasilkan dengan deriving, sedangkan class yang maknanya khusus ditulis sendiri. Instans Ord yang ditulis manual juga tetap terikat kewajiban instans Eq yang menyertainya, syarat yang sudah terpenuhi oleh klausa deriving tersebut.
Kesalahan umum#
Kesalahan pertama adalah menggunakan fungsi class pada tipe yang belum memiliki instans. Misalnya, mencetak sebuah fungsi dengan print ditolak compiler, karena tipe fungsi tidak memiliki instans Show. Pesan errornya menyebut class dan tipenya, misalnya No instance for (Show ...), dan perbaikannya adalah menyediakan instans melalui deriving atau penulisan manual, atau menghapus kebutuhan tersebut jika tipe memang tidak dapat ditampilkan.
Kesalahan kedua adalah menaruh class di luar daftar yang dapat dihasilkan. Klausa deriving hanya berlaku untuk kumpulan class tertentu, yaitu Eq, Ord, Enum, Bounded, Show, dan Read. Deklarasi deriving (Num) ditolak compiler karena Num berada di luar daftar itu, dan class buatan sendiri juga selalu diberi instans secara manual.
Kesalahan ketiga adalah mendeklarasikan dua instans untuk satu pasangan class dan tipe. Setiap tipe hanya boleh memiliki satu instans untuk satu class, sehingga deklarasi instans yang berulang ditolak compiler. Kebutuhan perilaku yang berbeda bagi satu tipe ditangani dengan membungkus tipe itu dalam tipe baru, sehingga setiap tipe tetap memiliki satu instans per class.
Ringkasan#
- Klausa
derivingmenghasilkan instans untukEq,Ord,Enum,Bounded,Show, danReadtanpa penulisan manual. - Hasil deriving
Ordmengikuti urutan penulisan konstruktor, dan hasilEnumsertaShowselaras dengan urutan tersebut. - Instans manual menetapkan makna perbandingan sendiri, misalnya melalui fungsi bobot.
- Fungsi class pada tipe tanpa instans ditolak compiler dengan pesan yang menyebut class dan tipenya.
- Satu tipe hanya boleh memiliki satu instans untuk satu class.
Referensi#
- Haskell 2010 Language Report — haskell.org — Bagian deklarasi class, instance, dan derived instances pada laporan resmi bahasa.
- Type class — Haskell Wiki — Ringkasan konsep type class dan pemanfaatan instans.
- GHC Users Guide — haskell.org — Panduan resmi pengguna GHC, termasuk perilaku compiler pada deklarasi instans.