Type class menyatakan sekumpulan perilaku yang dapat diikuti oleh banyak tipe. Fungsi yang mengharuskan tipenya mengikuti class tertentu tetap bersifat generik, tetapi hanya menerima tipe yang menyediakan perilaku tersebut. Topik ini membahas deklarasi class dan instans, class bawaan Eq, Ord, Show, dan Enum, serta penulisan constraint pada fungsi buatan sendiri.
Perilaku sebagai kontrak type class#
Sebuah type class, selanjutnya disebut class, mendeklarasikan nama-nama fungsi tanpa menetapkan tipe konkretnya. Tipe tertentu mengikuti class dengan menyediakan implementasi bagi seluruh fungsi yang disyaratkan, dan pasangan tipe dengan implementasi itu disebut instans (instance). Dengan pola ini, satu nama fungsi dapat bekerja untuk banyak tipe, sedangkan setiap tipe menyediakan cara kerja yang sesuai dengan isinya.
Program berikut mendeklarasikan class Deskripsi yang menuntut satu fungsi bernama deskripsi, kemudian menyediakan tiga instans untuk tipe Int, Bool, dan Double. Setiap instans menuliskan deskripsi yang berbeda sesuai jenis nilainya.
-- Class menyatakan kontrak: tipe yang mengikutinya menyediakan fungsinya
class Deskripsi a where
deskripsi :: a -> String
-- Instans untuk tipe konkret: satu kontrak, implementasi berbeda-beda
instance Deskripsi Int where
deskripsi n = "bilangan bulat bernilai " ++ show n
instance Deskripsi Bool where
deskripsi b = if b then "nilai kebenaran benar" else "nilai kebenaran salah"
instance Deskripsi Double where
deskripsi x = "bilangan desimal bernilai " ++ show x
main :: IO ()
main = do
putStrLn (deskripsi (7 :: Int))
putStrLn (deskripsi True)
putStrLn (deskripsi (2.5 :: Double))bilangan bulat bernilai 7
nilai kebenaran benar
bilangan desimal bernilai 2.5Pemanggilan deskripsi (7 :: Int) menyertakan anotasi tipe pada nilainya agar pemilihan instans menjadi pasti. Apabila anotasi tidak diberikan, compiler tidak memiliki dasar untuk memilih Int daripada Double, dan program ditolak karena variabel tipenya ambigu. Pemanggilan deskripsi True tidak memerlukan anotasi karena hanya ada satu instans yang cocok untuk Bool.
Eq dan Ord#
Class Eq menyediakan pemeriksaan kesetaraan melalui fungsi (==) dan (/=). Class Ord memperluas Eq dengan perbandingan urutan, antara lain <, >, max, min, dan compare. Fungsi compare menghasilkan tipe Ordering yang hanya memiliki tiga nilai, yaitu LT untuk kurang dari, EQ untuk sama, dan GT untuk lebih dari.
# pemeriksaan kesetaraan dari class Eq
ghc -e '5 == 5'
# pembanding dari class Ord menghasilkan LT, EQ, atau GT
ghc -e 'compare "api" "air"'
# anotasi tipe (==) menyertakan constraint Eq a
ghci -e ':t (==)'True
GT
(==) :: Eq a => a -> a -> BoolPada perbandingan teks, "api" disandingkan dengan "air", dan hasilnya GT karena huruf p berada setelah i pada urutan huruf. Anotasi (==) :: Eq a => a -> a -> Bool memperlihatkan hubungan constraint dengan fungsi generik, yaitu pemanggil dapat memilih a apa pun dengan syarat a memiliki instans Eq. Pola pembacaan yang sama berlaku untuk seluruh fungsi class, sehingga constraint dapat dibaca sebagai tiket masuk yang wajib dibawa tipenya.
Perbandingan Ord selalu mengandaikan kesetaraan Eq, hubungan yang disebut superclass, karena apa pun yang dapat diurutkan pasti dapat diperiksa kesetaraannya. Compiler menuntut instans Eq terlebih dahulu sebelum Ord boleh diberikan pada sebuah tipe. Ketentuan ini menjaga class-class bawaan tersusun dari yang paling dasar ke yang memperluasnya.
Show dan Enum#
Class Show menyediakan fungsi show yang mengubah nilai menjadi String, sedangkan class Enum menyediakan barisan nilai melalui fungsi succ, fromEnum, dan notasi rentang seperti [1 .. 5]. Dengan keduanya, nilai dapat ditampilkan ke layar dan posisinya dalam barisan dapat dihitung.
# show mengubah nilai menjadi String, termasuk nilai majemuk
ghc -e 'show (3, True)'
# succ mengambil nilai berikutnya pada barisan Enum
ghc -e "succ 'a'"
# notasi rentang menyusun barisan dengan Enum
ghc -e '[1 .. 5]'"(3,True)"
'b'
[1,2,3,4,5]Nilai tuple (3, True) dapat ditampilkan karena kedua komponennya memiliki instans Show. Pemanggilan succ 'a' menghasilkan 'b' sesuai urutan huruf pada tipe Char, sedangkan rentang [1 .. 5] menyusun bilangan dari 1 sampai 5. Tipe dengan instans Enum dapat disusun menjadi barisan, dan fromEnum mengubah nilai menjadi nomor urutnya, misalnya fromEnum True menghasilkan 1.
Class-class bawaan juga berpasangan pada beberapa tempat. Tipe yang memiliki instans Enum sekaligus Bounded dapat menyatakan rentang penuhnya melalui minBound dan maxBound, bentuk yang lazim digunakan untuk menyebut seluruh nilai satu tipe tanpa menulisnya satu per satu. Sementara itu, Show pada tipe majemuk bekerja dengan menyerahkan pencetakan kepada instans Show milik komponennya, sehingga show (3, True) dapat berjalan tanpa instans khusus untuk tuple. Pola penyerahan kepada komponen semacam ini berlaku luas, sehingga tipe buatan sendiri yang fieldnya sudah memiliki instans cukup mengikutinya.
Constraint pada fungsi sendiri#
Constraint juga ditulis pada fungsi buatan sendiri. Fungsi adaDiDalam memeriksa keanggotaan satu nilai dalam sebuah list, dan pemeriksaan itu membutuhkan kesetaraan, sehingga constraint Eq a wajib menyertainya. Tanpa constraint, pemanggilan (== x) tidak memiliki jaminan bahwa x dapat dibandingkan.
-- Eq a => : fungsi hanya berlaku untuk tipe yang dapat dibandingkan
adaDiDalam :: Eq a => a -> [a] -> Bool
adaDiDalam x ys = any (== x) ys
main :: IO ()
main = do
print (adaDiDalam 3 [1, 2, 3, 4])
print (adaDiDalam 9 [1, 2, 3, 4])
print (adaDiDalam "Senin" ["Senin", "Rabu"])
print (adaDiDalam True [False, False])True
False
True
FalseTipe adaDiDalam adalah Eq a => a -> [a] -> Bool, dan fungsi tersebut bekerja untuk list Int, String, maupun list Bool tanpa perubahan apa pun. Pemanggilan hanya diizinkan untuk tipe yang memiliki instans Eq, sehingga compiler menolak pemanggilan dengan tipe yang tidak dapat dibandingkan. Pemisahan antara “apa yang dilakukan” pada kontrak class dan “bagaimana melakukannya” pada instans tiap tipe membuat fungsi tetap generik tanpa mengorbankan pemeriksaan tipe.
Ringkasan#
- Type class adalah kontrak perilaku, dan instans adalah pasangan tipe dengan implementasi seluruh fungsi class tersebut.
Eqmenyediakan(==)dan(/=), sedangkanOrdmenambahkan urutan dengancompareyang menghasilkanLT,EQ, atauGT.Showmengubah nilai menjadiString, danEnummenyediakan nilai berikutnya serta notasi rentang.- Constraint seperti
Eq a =>membatasi pemilihan tipe pada fungsi generik, dan compiler memeriksanya pada setiap pemanggilan. - Instans dideklarasikan per tipe, sehingga satu fungsi class melayani banyak tipe dengan cara kerja berbeda.
Referensi#
- Type class — Haskell Wiki — Penjelasan konsep type class dan perbedaannya dengan class berorientasi objek.
- Haskell 2010 Language Report — haskell.org — Spesifikasi deklarasi class dan instance pada laporan resmi bahasa.
- Prelude — Hackage
— Dokumentasi class
Eq,Ord,Show, danEnumbeserta fungsinya.