Belajar Haskell - Tipe Data & Type Inference
Episode 5 of 23

Belajar Haskell - Tipe Data & Type Inference

Mengenal tipe data dasar Haskell seperti Int, Integer berpresisi arbitrari, Double, Bool, Char, String dan tuple, memahami bagaimana compiler menebak tipe lewat type inference Hindley-Milner, menulis signature eksplisit dengan ::, serta menguasai kurung dan currying pada tipe fungsi berparameter banyak.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 4 kalian menulis definisi fungsi, guards, where/let, dan memahami aturan layout, saatnya memahami bahasa tipe Haskell. Jika sintaks adalah tata kalimat, tipe adalah semantiknya: tipe menentukan apa yang boleh dan tidak boleh dilakukan sebuah ekspresi, dan compiler-lah yang menegakkannya sebelum program berjalan.

Mengapa bab ini penting? Karena di Haskell, tipe adalah dokumentasi, kontrak, dan bug-detector sekaligus. Signature yang kalian tulis menjadi spesifikasi perilaku fungsi; compiler memeriksa bahwa implementasi mematuhinya. Sebagian besar waktu debugging kalian akan dihabiskan untuk "berdialog" dengan type checker — semakin paham bahasanya, semakin cepat kalian menyelesaikan masalah.

Tipe Data Dasar

TipeDeskripsiContoh
IntBilangan bulat 64-bit (fixed width)42
IntegerBilangan bulat presisi arbitrer (tak terbatas)12345678901234567890
DoubleFloating point presisi ganda3.14159
BoolBooleanTrue, False
CharSatu karakter'a'
StringList of Char (alias [Char])"halo"
(a, b)Tuple — pasangan nilai (heterogen)(1, "satu")

Pembedaan paling penting untuk bulat: Int dibatasi lebar bit (bisa overflow), sementara Integer bisa menampung angka sebesar apa pun selama memori cukup. Untuk komputasi yang butuh presisi penuh — misalnya kriptografi atau keuangan — Integer adalah pilihan aman.

String adalah List of Char

Satu fakta yang menjelaskan banyak hal: String hanyalah alias dari [Char] — list berisi karakter. Ini berarti semua fungsi list (yang akan kita bahas di episode 6) langsung bekerja pada String:

String adalah [Char]
halo :: String
halo = "halo"
 
pertama :: Char
pertama = head "halo"      -- 'h'
 
terbalik :: String
terbalik = reverse "halo"  -- "olah"

head mengambil elemen pertama list, reverse membaliknya — keduanya fungsi list biasa yang "tanpa tahu" kalau list-nya berisi karakter.

Type Inference: Compiler Menebak Tipe

Kekuatan besar Haskell: kalian tidak wajib menulis tipe. Algoritma Hindley-Milner menebaknya dari penggunaan:

Tanpa signature - tipe ditebak
addOne x = x + 1

Tanyakan ke GHCi:

Melihat hasil inferensi
ghci> let addOne x = x + 1
ghci> :t addOne
addOne :: Num a => a -> a

GHC menjawab Num a => a -> a — artinya: "untuk tipe a apa pun yang merupakan anggota typeclass Num, fungsi ini menerima a dan mengembalikan a". Compiler menemukan bahwa + hanya bekerja untuk tipe numerik, jadi ia meng-generalisasi tipe addOne selama memenuhi batasan itu. Inilah contoh typeclass yang akan kita bedah di episode 8.

Karena tipe ditebak umum (polymorphic), menulis signature eksplisit akan mempersempit tipe. addOne :: Int -> Int membuat fungsi hanya bekerja untuk Int — pilihan yang tepat kalau memang hanya itu yang dibutuhkan, karena compiler akan mengingatkan saat fungsi dipanggil dengan tipe lain.

Tip

Aturan praktis Haskeller: tulis signature untuk semua fungsi top-level. Type inference bekerja tanpa signature, tetapi signature menjadi kontrak yang dibaca manusia dan diperiksa compiler. Ketika refactor berantakan, compiler menunjuk baris yang melanggar kontrak.

Signature adalah deklarasi tipe eksplisit, memakai operator :: yang dibaca "bertipe":

Signature eksplisit
luasPersegi :: Int -> Int
luasPersegi sisi = sisi * sisi
 
sapa :: String -> String
sapa nama = "Halo, " ++ nama ++ "!"

++ adalah operator konkatenasi string. Perhatikan bahwa menulis signature di atas definisi adalah konvensi; compiler mengecek kesesuaian keduanya. Signature salah + definisi benar = error kompilasi — inilah yang dimaksud "tipe sebagai kontrak".

Kurung dan Currying pada Tipe Fungsi

Tipe Int -> Int -> Int berarti: fungsi yang menerima Int, menghasilkan fungsi Int -> Int. Operator -> bersifat right-associative: Int -> Int -> Int dibaca Int -> (Int -> Int).

Currying dalam aksi
tambah :: Int -> Int -> Int
tambah x y = x + y
 
plusSepuluh :: Int -> Int
plusSepuluh = tambah 10

plusSepuluh = tambah 10 memakai partial application: kita memberikan satu argumen dan menyimpan fungsi yang tersisa. Tanpa menulis parameter sama sekali, plusSepuluh adalah fungsi Int -> Int. Ini adalah kunci fleksibilitas pemrograman fungsional — fungsi bisa dirakit seperti bahan bangunan.

Sebaliknya, jika fungsi menerima satu argumen yang kebetulan bertipe fungsi (higher-order), tanda kurung wajib:

Fungsi sebagai argumen
applyTwice :: (Int -> Int) -> Int -> Int
applyTwice f x = f (f x)

Di sini (Int -> Int) dalam kurung adalah parameter pertama: applyTwice menerima fungsi, lalu menerapkan-nya dua kali. applyTwice (*2) 5 menghasilkan 20.

Tuple: Mengelompokkan Nilai Berbeda

Tuple mengelompokkan nilai dengan tipe berbeda dalam satu nilai komposit:

Tuple dengan tipe berbeda
user :: (String, Int)
user = ("devan", 29)
 
koordinat :: (Double, Double)
koordinat = (-6.2, 106.8)

Tuple punya ukuran tetap (dua elemen di atas, bisa tiga, empat, dst.) dan tiap posisi punya tipe tersendiri: ("devan", 29) bertipe (String, Int), bukan (Int, String) — urutan dan tipe menentukan tipe tuple. Akses elemen pertama lewat fst dan kedua lewat snd:

Akses elemen tuple
nama :: String
nama = fst user   -- "devan"

Untuk tuple lebih dari dua elemen, pattern matching (episode 6) adalah cara yang lebih nyaman.

Kesalahan Umum (Common Pitfalls)

  1. Type mismatch paling klasik: mencampur Int dengan Double. x + 1 gagal jika x :: Double — Haskell tidak mengubah tipe secara implisit. Konversi eksplisit memakai fromIntegral, round, floor, dan ceiling.
Konversi numerik eksplisit
rataRata :: [Int] -> Double
rataRata xs = fromIntegral (sum xs) / fromIntegral (length xs)
  1. Lupa tanda kurung di argumen fungsi: applyTwice (*2) 5 membutuhkan (*2) dalam kurung; applyTwice *2 5 tidak akan terkompilasi.
  2. String dengan satu kutip: 'halo' adalah Char (error karena lebih dari satu karakter); selalu gunakan "..." untuk String.

Penutup

Inti yang harus dibawa pulang:

  • Tipe dasar: Int (fixed width), Integer (arbitrary precision), Double, Bool, Char, String (alias [Char]), dan tuple (a, b).
  • Type inference (Hindley-Milner) menebak tipe; :t di GHCi menampilkannya.
  • Tulis signature eksplisit dengan :: — ini kontrak yang diperiksa compiler.
  • -> bersifat right-associative; a -> b -> c adalah a -> (b -> c) — dasar currying dan partial application.
  • Tidak ada konversi numerik implisit; gunakan fromIntegral untuk angka.

Di episode 6 selanjutnya kita akan membahas lists, tuples & pattern matching — list homogen [a], list comprehension, fungsi map/filter/foldl/foldr, dekonstruksi list dengan (x:xs), serta pola pada tuple dan guards dengan wildcard. Sampai jumpa di episode 6!

Belajar Haskell - Tipe Data & Type Inference | Belajar Haskell