Mendalami struktur data paling penting di Haskell: list homogen [a] dengan list comprehension, fungsi higher-order map/filter/foldl/foldr, tuple sebagai nilai komposit yang bisa menyimpan tipe berbeda, serta pattern matching untuk mendekonstruksi list dengan (x:xs), tuple dan wildcard _ termasuk perulangan dengan rekursi.

Setelah di episode 5 kalian memahami tipe dasar dan type inference, saatnya mempelajari struktur data yang paling sering dipakai di seluruh ekosistem Haskell: list dan tuple. Hampir semua komputasi data — parsing, transformasi, pipeline, sampai query database — berputar di sekitar list dan fungsi-fungsi yang bekerja padanya.
Mengapa bab ini penting? Karena list adalah cara Haskell menggantikan perulangan for/while. Alih-alih "loop sambil mengubah array", kalian mentransformasi list dengan map, menyaring dengan filter, dan mereduksi dengan fold. Setelah episode ini, kalian akan membaca kode Haskell seperti pipeline data — dan pola inilah yang dipakai di episode 14 untuk streaming.
List di Haskell ditulis dalam kurung siku dan harus berisi tipe yang sama:
angka :: [Int]
angka = [1, 2, 3, 4, 5]
kata :: [String]
kata = ["apel", "jeruk", "mangga"]List bisa dibangun dengan operator kons : (cons) dan diakhiri list kosong []:
[1, 2, 3] == 1 : 2 : 3 : []1 : 2 : 3 : [] adalah bentuk kanonik list: kons bersifat right-associative, jadi [1,2,3] adalah 1 : (2 : (3 : [])). Ini penting karena pattern matching pada list justru bekerja pada struktur ini.
Range menghasilkan list berurutan secara elegan:
limaPertama :: [Int]
limaPertama = [1..5] -- [1,2,3,4,5]
genap :: [Int]
genap = [2,4..10] -- [2,4,6,8,10]List comprehension adalah sintaks berbasis set-builder notation untuk membangun list dari list lain — persis seperti notasi matematika:
kuadratGenap :: [Int]
kuadratGenap = [x * x | x <- [1..10], x `mod` 2 == 0]Dibaca: "untuk setiap x yang diambil dari [1..10], jika x mod 2 sama dengan 0, hasilkan x * x". Hasilnya [4,16,36,64,100]. Kalian juga bisa mengkombinasikan beberapa generator untuk menghasilkan produk kartesian.
Fungsi yang menerima fungsi lain sebagai argumen disebut higher-order function. Tiga yang paling fundamental:
doubleAll :: [Int] -> [Int]
doubleAll xs = map (*2) xsmap f xs menerapkan f ke setiap elemen dan mengembalikan list hasil. Jenis fungsi total: input list apa pun selalu menghasilkan output valid.
onlyEven :: [Int] -> [Int]
onlyEven xs = filter even xsfilter p xs mempertahankan elemen yang membuat predikat p bernilai True.
fold mereduksi list menjadi satu nilai. foldl mengakumulasi dari kiri, foldr dari kanan:
jumlah :: [Int] -> Int
jumlah xs = foldl (+) 0 xs
concatAll :: [[Int]] -> [Int]
concatAll xss = foldr (++) [] xssfoldl (+) 0 [1,2,3] adalah ((0 + 1) + 2) + 3. foldr adalah fondasi komposisi list yang paling fleksibel — di Haskell, map dan filter bisa ditulis ulang sebagai foldr. Perbedaan halus keduanya (soal laziness dan space leak) akan kita bahas di episode 21.
Note
Pola yang paling sering kalian lihat di kode produksi Haskell adalah rantai pipeline: filter lalu map lalu fold. Contoh nyatanya akan muncul di episode 14 (streaming data) dan episode 15 (query database) — kuasai ketiga fungsi ini dan kalian sudah menguasai 80% gaya menulis Haskell.
Berbeda dari list yang homogen, tuple mengelompokkan nilai dengan tipe berbeda dalam satu nilai komposit:
user :: (String, Int)
user = ("devan", 29)
koordinat :: (Double, Double)
koordinat = (-6.2, 106.8)Tuple punya ukuran tetap 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:
nama :: String
nama = fst user -- "devan"Untuk tuple lebih dari dua elemen, pattern matching adalah cara yang jauh lebih nyaman.
Pattern matching membalik proses konstruksi: memecah data sesuai bentuk asalnya. Untuk list, pola paling penting adalah (x:xs) — "elemen pertama x, sisa xs":
headOf :: [Int] -> Int
headOf (x:xs) = x
tailOf :: [Int] -> [Int]
tailOf (x:xs) = xsDengan (x:xs), kalian bisa menulis rekursi — pengganti for loop di Haskell:
sumList :: [Int] -> Int
sumList [] = 0
sumList (x:xs) = x + sumList xssumList [] = 0 adalah base case; sumList (x:xs) = x + sumList xs memecah list, menjumlahkan elemen pertama, lalu berlanjut ke sisa. Inilah cara berpikir perulangan di Haskell: base case + langkah rekursif.
Tuple dan wildcard _ ikut serta dalam pola:
describeUser :: (String, Int) -> String
describeUser (nama, umur) = nama ++ " berumur " ++ show umur
namaPertama :: (String, Int) -> String
namaPertama (nama, _) = nama(nama, _) memecah tuple dan mengabaikan elemen kedua. Wildcard _ dipakai setiap kali kita tidak peduli dengan nilai di posisi itu — misalnya sebagai fallback pada pola yang tidak lengkap.
Pattern dan guards sering dipakai bersamaan:
sign :: Int -> String
sign x
| x > 0 = "positif"
| x < 0 = "negatif"
| otherwise = "nol"Untuk list yang lebih spesifik, kalian bisa mengkombinasikan beberapa pola:
describe :: [Int] -> String
describe [] = "kosong"
describe [x] = "satu elemen: " ++ show x
describe (x:y:_) = "minimal dua elemen"Baris ketiga (x:y:_) hanya mencocokkan list dengan minimal dua elemen — _ menangkap sisa yang tidak kita pedulikan.
headOf [] crash dengan "non-exhaustive patterns". Selalu sediakan kasus [].[1, "satu"] tidak terkompilasi — list harus homogen; gunakan tuple atau ADT (episode 8) untuk mencampur tipe.foldl pada list tak hingga — dengan evaluasi lazy, foldl bisa hang. Praktik baik modern: foldl' (strict) dari Data.List. Kita bahas di episode 21._ paling akhir.Inti yang harus dibawa pulang:
[a], dibangun dengan : dan [].[ekspr | x <- xs, kondisi] membangun list secara deskriptif.(a, b) mengelompokkan tipe berbeda; akses dengan fst/snd atau pattern matching.(x:xs) mendekonstruksi list; kombinasikan dengan guards dan wildcard _.Di episode 7 selanjutnya kita akan membahas purity, laziness & evaluasi — mengapa fungsi pure membuat kode mudah diuji dan diparalelkan, bagaimana evaluasi lazy memungkinkan infinite list [1..] dengan take/cycle, serta kapan harus memaksa evaluasi dengan $! dan bang patterns untuk performa. Sampai jumpa di episode 7!