Pemrograman Haskell
Pemrograman Haskell
Pemrograman Haskell
Disusun Oleh :
Musofa 672011605
Danang Kurniawan 672012606
Program Studi Teknik Informatika
Faktultas Teknologi Informasi
Universitas Kristen Satya Wacana
Salatiga
2013
Pemrograman Haskell
Haskell merupakan bahasa pemrograman yang fungsional, malas dan murni. Ia disebut
malas karena tidak mengevaluasi ekspresi-ekspresi yang digunakannya yang sebenarnya
memang tidak diperlukan untuk menentukan jawaban bagi suatu masalah. Kebalikan dari malas
adalah teliti yang merupakan strategi pengevaluasian bagi kebanyakan bahasa pemrograman
(C, C++, Java, bahkan ML). Salah satu ciri dari bahasa yang teliti adalah setiap ekspresi diteliti
apakah hasil dari perhitungan tersebut penting atau tidak.(Hal ini mungkin tidak mutlak benar
seluruhnya karena adanya pengoptimalan compiler yang sering disebut denganeleminasi kode
matiyang akan menghilangkan ekspresi yang tidak digunakan di program yang sering dilakukan
oleh bahasa-bahasa tersebut). Haskell disebut murni karena bahasa ini tidak memperbolehkan
adanya efek samping (Efek samping adalah sesuatu yang mempengaruhi bagian di program.
Misalnya suatu fungsi yang mencetak sesuatu ke layar yang mempengaruhi nilai dari variabel
global. Tentu saja, suatu bahasa pemrograman yang tanpa efek samping akan menjadi sangat
tidak berguna; Haskell menggunakan sebuah system monads untuk mengisolasi semua
komputasi kotor dari program dan menampilkannya dengan cara yang aman. Haskell disebut
bahasa fungsional karena evaluasi dari programnya sama dengan mengevaluasi sebuah fungsi
dalam bahasa matematika murni. Hal ini juga yang membedakannya dari bahasa standard
(seperti C dan Java) yang mengevaluasi sederetan pernyataan secara urut (inilah pola dari bahasa
terstruktur ) Yang termasuk dalam bahasa pemrograman fungsional antara lain Lisp, Scheme,
Erlang, Clean, Mercury, ML, OCaml, SQL, XSL dan lain-lain. Di antara bahasa fungsional
tersebut, Haskell merupakan bahasa yang ideal dalam banyak hal. Bahasa fungsional seperti juga
bahasa logika seperti Prolog adalah deklaratif. Kebalikannya yaitu bahasa procedural dan bahasa
yang berorientasi pada obyek adalah terstruktur. Haskell memiliki sintak yang lebih mudah
untuk dipahami daripada Lisp--bahasa turunan--(terutama bagi pemrogram yang pernah
menggunakan bahasa yang menggunakan tanda baca yang sederhana/ringan seperti Python, TCL
and REXX). Kebanyakan operatornya bersifat infix, tapi ada juga yang prefix. Pengaturan
indentasi dan modulnya sangatlah familiar dan mungkin sangat menarik, misalnya tanda kurung
bersarang yang terlalu dalam (seperti yang terlihat dalam Lisp) dihindari.
Sejarah Pemrograman Haskell
Pada bulan September tahun 1987 diadakan pertemuan dalam konferensi Bahasa
Pemrograman Fungsional dan Arsitektur Komputer atau Functional Programming Languages
and Computer Architecture (FPCA 87) di Portland, Oregon, untuk membicarakan keadaan yang
kurang menguntungkan di komunitas pemrograman fungsional yaitu adanya selusin bahasa
pemrograman fungsional yang murni, kesemuanya mirip dalam hal bentuk ekspresi dan
sintaknya. Telah dihasilkan suatu konsensus yang kuat dalam pertemuan itu yaitu bahwa
penyebarluasan kegunaan dari kelas bahasa fungsional ini telah dihalangi oleh kekurangan dari
kebanyakan bahasa. Telah diputuskan juga bahwa harus ada sebuah komite yang dibentuk untuk
merancang suatu bahasa yang menyediakan komunikasi gagasan baru yang lebih cepat, suatu
dasar yang stabil bagi pengembangan aplikasi nyata dan sebuah cara untuk membuat orang
tertarik menggunakan bahasa fungsional. Kemudian diciptakanlah sebuah bahasa pemrograman
fungsional yang murni yang dinamai Haskell. Nama tersebut diambil dari nama seorang ahli
logika yaitu Haskell B. Curry yang telah menciptakan banyak dasar-dasar logika bagi kita.
Tujuan utama dari komite tersebut adalah untuk merancang bahasa yang memenuhi syarat
sebagai berikut :
1. Harus cocok untuk pengajaran, penelitian dan aplikasi termasuk untuk membuat sistem
yang besar.
2. Harus seluruhnya dijelaskan melalui sintak dan semantik yang formal.
3. Harus tersedia secara bebas. Semua orang boleh mengimplementasikan bahasa tersebut
dan menyebarkannya ke siapa saja yang mereka sukai.
4. Harus didasarkan pada gagasan yang sesuai dengan konsensus umum.
5. Harus mengurangi perubahan yang tidak diperlukan dalam bahasa pemrograman
fungsional.
Komite tersebut menganggap bahwa Haskell dapat dijadikan sebagai bahan penelitian masa
depan dalam bidang rancangan bahasa dan berharap bahwa variasi bahasa akan muncul sesuai
dengan hasil percobaan. Haskell kemudian terus berkembang sejak dipublikasikan. Pada
pertengahan tahun 1997, telah ada empat rancangan bahasa yang dihasilkan (yang terbaru pada
saat itu adalah Haskell 1.4). Pada saat workshop Haskell tahun 1997 di Amsterdam, diputuskan
bahwa diperlukan varian yang stabil dari Haskell. Bahasa yang stabil ini disebut Haskell 98.
Haskell 98 mirip dengan Haskell 1.4, tapi ia menawarkan beberapa penyederhanaan dan
penghilangan lubang-lubang jebakan karena ketidakhati-hatian.
Setelah Haskell 98 stabil, kelihatan jelas sekali bahwa banyak program memerlukan akses ke
suatu set pustaka fungsi yang lebih banyak (yakni berhubungan dengan input/output dan
interaksi ringan dengan sistem operasi). Jika program ini menjadi fleksibel, sekumpulan pustaka
harus distandarkan juga. Sejumlah usaha dilakukan oleh komite untuk memperbaiki pustaka
Haskell 98.
Haskell
Haskell merupakan salah satu dari banyak bahasa pemrograman fungsional, seperti Lisp,
Scheme, Erlang, Clean, Mercury, ML, Ocaml, SQL, XSL, dll. Mirip dengan bahasa
pemrograman fungsional, bahasa pemrograman logical seperti Prolog merupakan bahasa
pemrograman deklaratif. Berlawanan dengan kedua kategori di atas, bahasa pemrograman
prosedural dan object-oriented masuk dalam kategori bahasa pemrograman terstruktur.
Dikatakan terstruktur, karena mengandung urutan perintah yang dieksekusi satu demi satu
secara strict (keras). Beberapa bahasa pemrograman seperti Python, Perl, dan Ruby melewati
batasan-batasan paradigma di atas.
Di antara bahasa pemrograman fungsional, Haskell dalam banyak hal merupakan yang
paling ideal. Haskell merupakan bahasa pemrograman fungsional murni, yang berarti jauh dari
efek samping. Bahasa pemrograman fungsional merupakan ekspresi tunggal, yang dieksekusi
dengan mengevaluasi ekspresi tersebut. Bahasa pemrograman fungsional mirip dengan
spreadsheet, di mana nilai dari tiap cell ditetapkan yang berpengaruh pada nilai dari cell lainnya.
Fokusnya adalah pada apa yang akan dihitung, bukan bagaimana proses penghitungannya.
Adapun kelebihan dari bahasa pemrograman fungsional antara lain:
1. Singkat.
Program fungsional cenderung lebih ringkas dibanding program terstruktur (2 sampai 10
kali).
2. Mudah dimengerti.
Program fungsional seringjali lebih mudah untuk dimengerti. Dalam kasus Quicksort,
tidak terlalu diperlukan pengetahuan mengenai Haskell maupun quicksort. Hal tersebut
tidak dapat terjadi pada program C, yang membutuhkan waktu untuk dimengerti, dan
sangat mudah untuk melakukan kesalahn kecil yang mengakibatkan program tidak dapat
dieksekusi.
3. Tidak ada tumpukan pada memori.
Tidak ada kemungkinan memperlakukan integer sebagai pointer, atau dilanjutkan dengan
pointer null.
4. Kode dapat digunakan kembali.
Bahasa pemrograman fungsional menggunakan polymorphism, yang akan meningkatkan
penggunaan kembali kode.
5. Pelekatan yang kuat.
Bahasa fungsional non-strict memliki fitur kuat lainnya: hanya mengevaluasi program
seperti yang diperlukan untuk menghasilkan jawaban, yang sering disebut dengan lazy
evaluation. Struktur data dievaluasi secukupnya untuk menghasilkan jawaban, dan
sebagian mungkin tidak dievaluasi sama sekali. Hal ini memungkinkan pelekatan untuk
menyusun bersama program yang sudah ada. Hal ini memungkinkan penggunaan
kembali program, atau potongan program lebih sering dari yang dapat dilakukan oleh
pengaturan terstruktur.
6. Abstraksi yang kuat.
Secara umum bahasa fungsional menawarkan cara-cara baru yang kuat untuk meng-
enkapsulasi abstraksi. Abstraksi mengizinkan kita untuk menentukan objek yang
pekerjaan internalnya tersembunyi. Abstraksi merupakan kunci untuk membangun
program modular yang dapat dipelihara. Satu mekanisme abstraksi yang kuat yang
terdapat dalam bahasa fungsional adalah higher-order function.
7. Manajemen memori yang terintegrasi.
Kebanyakan program rumit perlu mengalokasikan memori dinamis dari tumpukan (heap).
Setiap bahasa fungsional memudahkan pemrogram dari beban manajemen penyimpanan
tersebut. Penyimpanan dialokasikan dan diinisialisaikan secara implisit, dan diselamatkan
secara otomatis oleh kolektor sampah.
Mengapa Menggunakan Haskell
Menulis system software yang besar sangatlah sulit dan mahal. Dan bahkan untuk mengelola
system itu bisa lebih sulit dan lebih mahal. Bahasa pemrograman fungsional semacam Haskell
dapat membuatnya lebih mudah dan lebih murah. Haskell sebagai bahasa pemrograman
fungsional murni dapat :
1. Meningkatkan produktifitas programmer (Ericsson banyak memanfaatkan hasil
percobaan Haskell dalam software telephony)
2. Lebih singkat, lebih jelas dan kode-kodenya mudah dibaca
3. Errornya semakin sedikit dan reabilitynya lebih tinggi
4. Membuat jarak antara programmer dengan bahasa itu lebih pendek
5. Waktu untuk membuat program menjadi lebih singkat
Banyak alasan untuk menggunakan Haskell diantaranya adalah kita bisa menulis kode bebas
atau bugs-free code dalam waktu yang lebih singkat dibanding bahasa lainnya. Bahasa ini terus-
menerus berkembang dan kian hari makin banyak kelebihannya. Selain itu, Haskell cocok untuk
berbagai macam aplikasi.
Sebagai contoh:
Quicksort dalam Haskell:
qsort[] = []
qsort(x:xs)= qsort elts_lt_x ++ [x] ++ qsort elts_lt_greq_x
where
elts_lt_x = [y|y<-xs, y<x]
elts_greq_x = [y|y<-xs, y>= x]
Quicksort dalam C:
qsort(a, lo, hl) int a[], hi, lo;
{
int h, l, p, t;
if (lo<hi) {
l=lo;
h=hi;
p=a[hi];
do
while ((l<h) && (a[l]<=p))
l=l+1;
while ((h>l) && (a[h]>=p))
h=h-1;
if (l<h) {
t=a[l];
a[l]=a[h];
a[h]=t;
}
} while (l<h);
t=a[l];
a[l]=a[hi];
a[hi]=t;
qsort(a, lo, l-1);
qsort(a, l+1, hi);
}
}
Di samping itu yang paling penting adalah komunitas pengguna Haskell yang sangat
membantu dalam memecahkan permasalahan seputar pemrograman menggunakan Haskell.
Bahasa pemrograman ini secara konstan berkembang (bukan berarti bahasa pemrograman ini
tidak stabil, tetapi banyak perluasan yang telah ditambahkan pada beberapa compiler yang sangat
membantu pengguna) dan seringkali usul pengguna diperhatikan saat perluasan akan
diimplementasikan.
Mengapa Tidak Menggunakan Haskell
Ada dua kelemahan dari Haskell yaitu :
1. kode programnya cenderung agak lambat berkembang dibanding program yang sama
yang ditulis dalam bahasa seperti C
2. Haskell cenderung sulit untuk didebug.
Kelemahan kedua bukanlah masalah yang terlalu besar tetapi yang pertama agak
mengganggu, namun walaupun demikian CPU time selalu lebih murah daripada programmer
time dan tidak apalah jika harus menunggu sedikit lebih lama untuk melihat outputnya setelah
menyimpannya beberapa hari terhitung dari saat diprogram dan didebug. Namun Haskell
sekarang telah memiliki standard interface fungsi asing atau foreign-function interface yang
memperbolehkan kita untuk berhubungan dengan kode program yang ditulis dalam bahasa lain.
Keuntungan Haskell
Tidak ada variabel yang berubah
Salah satu dari kebiasaan pemrograman paling umum dalam pemrograman terstruktur
adalah untuk memberikan suatu nilai ke suatu variabel, kemudian memberinya suatu nilai
berbeda seperti contoh dalam bahasa C berikut ini
if (myVar==37) {...}
myVar += 2
for (myVar=0; myVar<37; myVar++) {...}
Namun dalam Haskell sangalah berbeda variabel untuk jenis ini tidak ada sama sekali.
Suatu nama bisa berupa nilai, tetapi sekali diberikan nilai, nama tersebut mewakili nilai itu di
seluruh program. Tidak boleh ada pengubahan. Dalam Haskell, " variabel" adalah kurang lebih
seperti variabel dalam persamaan matematika. Sebagai inspirasi, lihat contoh dalam persamaan
matematika di bawah ini:
10x + 5y - 7z + 1 = 0
17x + 5y - 10z + 3 = 0
5x - 4y + 3z - 6 = 0
Terlihat bahwa ada beberapa nama yang tidak diketahui nilainya berapa, akan tetapi yang
tidak diketahui tersebut tidak akan berubah samapai kita berhasil menemukan jawabannya.
Meniadakan efek samping
Dalam Haskell, perhitungan fungsi tidak bisa mempunyai efek samping di dalam
program itu. Kebanyakan efek samping di dalam program terstruktur mungkin jenis variabel
yang didefinisikan kembali dalam panel akhir ( baik variabel global, atau lokal, atau kamus,
daftar, atau struktur penyimpanan lain), tetapi tiap-tiap Peristiwa I/O adalah juga merupakan efek
samping. Disain baik mungkin memerlukan keluaran dan masukan itu hanya terjadi di dalam
suatu kesatuan terbatas dinamai fungsi. Sedikit pemrograman tersusun ditujukan untuk membaca
dan menulis ke STDIO, file, alat grafis, dll., disemua tempat dan dengan cara yang sukar untuk
diperkirakan. pemrograman fungsional mengandung lingkungan skrip tingkat yang lebih tinggi.
Tidak ada perulangan
Corak menarik Haskell yang lain adalah meniadakan segala konstruksi perulangan. Tidak
ada for dan tidak ada while. Tidak Ada GOTO atau branch atau jmp atau break. Orang akan
berpikir bahwa mustahil untuk mengendalikan pekerjaan suatu program tanpa konstruksi
(terstruktur) dasar seperti; tetapi membuang hal-hal ini benar-benar sungguh melegakan.
Ketiadaan perulangan sungguh sama halnya perihal tidak ada efek samping. Ketika masuk dalam
suatu perulangan maka tidak bisa mendefinisikan variabel-variabel dengan nilai yan berbeda, tak
ada yang membedakannya. Kebutuhan untuk percabangan adalah digunakan unutk membedakan
aktivitas program. Sedangkan pemrograman fungsional tidak memiliki aktivitas, hanya definisi-
definisi fungsi sehingga tidak memerlukan percabangan.tetapi tetap ada kemungkinan untuk
mensimulasikan hampir semua konstruksi perulangan umum, yang sering menggunakan kata
kunci sama seperti di bahasa yang lain, dan dalam suatu gaya yang sepertinya serupa ke
konstruksi terstruktur.
Tidak ada program order
Yang lain yang tidak dibutuhkan olah Haskell adalah program order. Satuan definisi yang
membenahi suatu program dapat terjadi dalam order manapun dan apapun juga. Untuk contoh,
ini mungkin nampak seperti suatu masalah:
Kutipan program :
-- Program excerpt
j = 1+i
i = 5
-- Hugs session after loading above program
-- Main> i
-- 5 :: Integer
-- Main> j
-- 6 :: Integer
Yang perlu dipahami dalam program seperti di atas adalah bahwa i dan j tidak diberikan
nilai-nilai, tetapi diberikan dari apa yang proses berikan. Sesungguhnya, i dan j adalah fungsi,
dan contoh di atas adalah definisi fungsi. Pada banyak bahasa pemprograman terstruktur, tidak
boleh mendefinisikan suatu fungsi lebih dari satu kali (setidaknya dalam lingkup yang sama).
Tipe Data
Sebelumnya akan dibahas dulu tipe-tipe data yang akan digunakan, yaitu Integer, Char,
dan Double, Bool, dan list.
Integer menyatakan bilangan bulat. Contoh Integer adalah -1, 0, dan 20.
Char menyatakan karakter (simbol-simbol yang digunakan untuk penulisan), yang contohnya
adalah a, ?, dan 4.
Double menyatakan bilangan floating point, yaitu tipe data bilangan yang bisa melambangkan
pecahan. Contohnya adalah 3.14, 0.0, dan -1.69. Perhatikan bahwa 0 adalah Integer sedangkan
0.0 merupakan floating point. 2.0, 5.0, dan 11.0 juga merupakan floating point. Tipe data
Integer tidak akan pernah dituliskan menggunakan tanda desimal (titik), sedangkan tipe data
floating point selalu dituliskan menggunakan tanda desimal.
Bool dapat bernilai True atau False (tidak keduanya).
List sendiri secara mudahnya adalah deretan dari data-data yang tipenya sama. Contohnya
adalah [1,2,3] yang merupakan list Integer.
Karena Haskell merupakan bahasa fungsional murni, semua perhitungan dilakukan
melalui evaluasi dari ekspresi (terminologi sintaks) untuk menghasilkan nilai-nilai (entitas
abstrak yang dianggap sebagai jawaban). Setiap nilai berasosiasi dengan satu tipe (secara intuisi
tipe dapat dianalogikan sebagai himpunan nilai-nilai). Ekspresi termasuk nilai-nilai atomik
seperti integer 5, karakter a, dan fungsi \x -> x+1, seperti halnya juga nilai-nilai terstruktur
seperti list [1,2,3] dan pasangan (b,4).
Seperti halnya ekspresi menandai nilai, ekspresi tipe merupakan terminologi sintaks yang
menandai tipe nilai. Ekspresi tipe termasuk tipe atomik Integer (integer dengan ketepatan tanpa
batas), Char (karakter), Integer->Integer (fungsi pemetaan Integer ke Integer), juga tipe
terstruktur [Integer] (list integer homogen) dan (Char,Integer) (pasangan integer dan karakter).
Semua nilai dalam Haskell adalah first-class- dapat dilewatkan sebagai argumen ke
fungsi, dikembalikan sebagai hasil, diletakkan dalam struktur data, dll. Tipe dalam Haskell, pada
sisi lain, bukan merupakan first class. Tipe dalam hal mendeskripsikan nilai, dan asosiasi dari
nilai dengan tipenya dinamakan typing. Typing dapat ditulis sebagai berikut:
5 :: Integer
a :: Char
inc :: Integer -> Integer
[1,2,3] :: [Integer]
(b,4) :: (Char,Integer)
:: dibaca bertipe.
Fungsi dalam Haskell biasanya digambarkan sebagai rangkaian persamaan. Sebagai
contoh fungsi inc dapat digambarkan sebagai persamaan tunggal:
inc n = n+1
Sebuah persamaan merupakan contoh dari sebuah deklarasi. Jenis lain dari deklarasi adalah type
signature declaration, di mana dapat dideklarasikan typing secara eksplisit untuk inc:
inc :: Integer -> Integer
Untuk tujuan pendidikan, jika akan diindikasikan sebuah ekspresi c
1
menghasilkan atau
menurunkan ekspresi atau nilai c
2
, maka ditulis:
c
1
c
2
Sebagai contoh:
inc(inc 3) 5
Sistem tipe statis pada Haskell menggambarkan hubungan formal antara tipe dan nilai.
Sistem tipe statis meyakinkan program Haskell type safe; yaitu pemrogram aman dari kesalahan
pemilihan tipe. Sebagai contoh tidak dapat dilakukan penjumlahan 2 karakter, sehingga ekspresi
a+b merupakan penulisan yang salah. Keuntungan utama dari bahasa bertipe statis adalah
semua kesalahan/error tipe terdeteksi saat program di-compile. Tidak semua kesalahan ditangkap
oleh sistem tipe; ekspresi seperti 1/0 dapat bertipe, tetapi akan menghasilkan kesalahan pada
waktu eksekusi. Tetapi sistem tipe masih menemukan kesalahan program pada waktu compile,
yang mengizinkan compiler untuk menghasilkan kode yang lebih efisien.
Sistem tipe juga memastikan bahwa user-supplied type signature adalah benar. Dalam
kenyataannya sistem tipe pada Haskell cukup kuat untuk mengizinkan pengguna untuk
menghindari menulis type signature sama sekali; dengan kata lain sistem tipe menyimpulkan tipe
yang benar untuk pengguna.
Tipe Polymorfism
Haskell juga menyertakan tipe polimorfis tipe yang dalam beberapa cara terukur di atas semua
tipe. Ekspresi tipe polimorfis menguraikan keluarga dari tipe-tipe. Sebagai contoh, ( a )[a]
adalah keluarga dari tipe di mana untuk setiap tipe a berisi tipe list dari a. List dari integer
(seperti [1,2,3]), list dari karakter ([a,b,c]), sekalipun list dari list integer, dll, adalah
anggota dari keluarga ini. (Sebagai catatan [2,b] bukan contoh yang valid karena tidak ada tipe
tunggal yang berisi 2 dan b).
List merupakan struktur data yang umum digunakan dalam bahasa fungsional, dan
merupakan sarana yang baik untuk menerangkan prinsip-prinsip dari polimorfisme. List [1,2,3]
dalam Haskell merupakan kependekan dari list 1:(2:(3:[])), di mana [] merupakan list kosong
dan : merupakan operator infix yang meletakkan argumen pertama di depan argumen kedua
(dalam hal ini list), atau dapat juga ditulis 1:2:3:[].
Sebagai contoh dari fungsi user-defined yang beroperasi pada list, pertimbangkan
masalah menghitung jumlah elemen dalam list berikut:
length :: [a] -> Integer
length [] = 0
length (x:xs) = 1 + length xs
Dari persamaan di atas terbaca: Panjang dari list kosong adalah 0, dan panjang dari list di mana
elemen pertamanya x dan sisanya xs adalah 1 ditambah panjang dari xs. (sebagai catatan xs
merupakan jamak dari x, dan harus dibaca demikian).
Walaupun secara intuitif, contoh di atas menyorot sebuah aspek penting dari Haskell,
yaitu pattern matching (pencocokan pola). Ruas kiri persamaan mengandung pola seperti [] dan
x:xs. Dalam sebuah fungsi aplikasi, pola-pola seperti ini dibandingkan dengan parameter aktual
(nyata) dengan cara intuitif ([] hanya cocok dengan list kosong, dan x:xs akan cocok dengan list
apa saja dengan minimal satu elemen, x dihubungkan dengan elemen pertama dan xs dengan
sisanya dalam list). Jika pencocokan sukses, ruas kanan dievaluasi dan dikembalikan sebagai
hasil dari aplikasi. Jika gagal, persamaan berikutnya akan dicoba, dan jika semua persamaan
gagal, maka hasilnya error.
Fungsi length di atas juga merupakan contoh dari sebuah fungsi polimorfis. Fungsi
tersebut dapat diterapkan pada list yang berisi elemen bertipe apa saja, sebagai contoh [Integer],
[Char], atau [[Integer]].
length [1,2,3] 3
length [a,b,c] 3
length [[1],[2],[3]] 3
Berikut adalah 2 contoh fungsi polimorfis dalam list. fungsi head mengembalikan elemen
pertama dari list, fungsi tail mengembalikan semua elemen kecuali elemen pertama.
head :: [a] -> a
head (x:xs) = x
tail :: [a] -> [a]
tail (x:xs) = xs
Tidak seperti fungsi length, fungsi-fungsi di atas tidak didefinisikan untuk semua nilai yang
mungkin dari argumen mereka. Runtime error (kesalahan saat program dijalankan) akan muncul
saat fungsi-fungsi tersebut diterapkan pada list kosong.
Dengan tipe polimorfis beberapa tipe lebih umum dibanding lainnya dalam hal himpunan
nilai yang didefinisikan lebih besar. Sebagai contoh tipe [a] adalah lebih umum dari [Char].
Dengan kata lain, tipe terakhir dapat diturunkan dari sebelumnya dengan substitusi yang cocok
untuk a. Dibandingkan dengan bahasa bertipe monomorfis seperti C, polimorfisme
meningkatkan ke-ekspresif-an, dan tipe inferensi meringankan beban tipe dari pemrogram.
Tipe prinsipal (utama) dari ekspresi atau fungsi adalah tipe yang paling tidak umum,
secara intuitif, berisi semua instan dari ekspresi. Sebagai contoh, tipe prinsipal dari fungsi
head adalah [a] -> a; [b] -> a, a -> a.
Tipe User-Defind
Kita dapat menentukan tipe data sendiri dalam Haskell menggunakan deklarasi data.
Tipe penting yang sudah dikenal oleh Haskell adalah nilai kebenaran:
data Bool = False | True
Tipe yang didefinisikan di sini adalah Bool yang mempunyai dua nilai, yaitu True dan False.
Tipe Bool adalah sebuah contoh dari tipe konstruktor, dan True dan False adalah data
konstruktor (atau konstruktor saja).
Dengan cara yang sama kita dapat mendefinisikan tipe warna:
data Color = Red | Green | Blue | Indigo | Violet
Bool dan Color dalam contoh di atas merupakan tipe enumerasi.
Berikut contoh dari tipe dengan hanya satu data konstruktor:
data Point a = Pt a a
Karena merupakan konstruktor tunggal, tipe seperti Point sering disebut tipe tuple, karena
merupakan produk kartesian (dalam hal ini biner) dari tipe lain. Berlawanan dengan itu, tipe
multi-konstruktor seperti Bool dan Color disebut union dari tipe sum.
Point merupakan contoh dari tipe polimorfis: untuk semua tipe t, ia mendefinisikan tipe
dari titik-titik kartesian yang menggunakan t sebagai tipe koordinat. Dalam contoh sebelumnya
[] juga merupakan tipe konstruktor. Pada tipe t (bertipe apa saja) dapat diterapkan [] untuk
menghasilkan tipe baru [t]. sintaks Haskell mengizinkan [] t ditulis sebagai [t]. -> juga
merupakan tipe konstruktor; diberikan 2 tipe t dan u, t -> u merupakan tipe dari fungsi pemetaan
elemen dari tipe t ke elemen dari tipe u.
Catatlah bahwa tipe dari konstruktor data biner Pt adalah a -> a -> Point a, sehingga
yang berikut adalah valid:
Pt 2.0 3.0 :: Point Float
Pt a b :: Point Char
Pt True False :: Point Bool
Pada sisi lain, ekspresi seperti Pt a 1 tidak valid karena a dan 1 berbeda tipe.
Penting untuk membedakan antara menerapkan data konstruktor untuk menghasilkan
nilai, dan menerapkan tipe konstruktor untuk menghasilkan tipe; yang pertama terjadi saat run-
time dan bagaimana kita menghitung dalam Haskell, di mana yang terakhir terjadi saat compile-
time.
Tipe konstruktor seperti Point dan data konstruktor seperti Pt adalah dalam namespaces
terpisah. Ini memungkinkan nama yang sama untuk digunakan oleh tipe konstruktor dan data
konstruktor, seperti berikut:
data Point a = Point a a
Tipe Rekursif
Tipe dapat juga rekursif, seperti tipe dalam pohon biner:
data Tree a = Leaf a | Branch (Tree a) (Tree a)
Di sini didefinisikan tipe polimorfis pohon biner yang mana elemen-elemennya adalah node Leaf
berisi nilai dari tipe a, atau node internal (branch) berisi dua sub-tree (rekursif).
Saat membaca deklarasi data seperti ini, ingat bahwa Tree adalah tipe konstruktor, di
mana Branch dan Leaf adalah data konstruktor. Di samping menciptakan koneksi antara
konstruktor-konstruktor ini, deklarasi di atas mendefinisikan tipe berikut untuk Branch dan
Leaf:
Branch :: Tree a -> Tree a -> Tree a
Leaf :: a -> Tree a
Dengan contoh di atas telah didefinisikan suatu tipe yang cukup kaya untuk
mendefinisikan beberapa fungsi (rekursif) yang menggunakannya. Sebagai contoh akan
didefinisikan sebuah fungsi fringe yang mengembalikan sebuah list dari semua elemen dalam
daun dari sebuah pohon dari kiri ke kanan. Biasanya akan sangat membantu jika dituliskan
terlebih dulu tipe dari fungsi baru; dalam kasus ini tipe seharusnya Tree a -> [a]. fringe
merupakan fungsi polimorfis di mana untuk semua tipe a, memetakan pohon dari a ke list dari a.
Definisi yang tepat sebagai berikut:
fringe :: Tree a -> [a]
fringe (Leaf x) = [x]
fringe (Branch left right) = fringe left ++ fringe right
Di sini ++ merupakan operator infix yang menggabungkan dua list.
Tipe Sinonim
Untuk kenyamanan, Haskell menyediakan cara untuk mendefinisikan tipe sinonim, yaitu nama
untuk tipe yang sering dipakai. Tipe sinonim dibuat menggunakan deklarasi type. Berikut
beberapa contoh:
type String = [Char]
type Person = (Name,Address)
type Name = String
data Address = None | Addr String
Tipe sinonim tidak mendefinisikan tipe baru, tetapi memberi nama baru kepada tipe-tipe
yang sudah ada. Sebagai contoh tipe Person -> Name setara dengan (String,Address) ->
String. Nama yang baru seringkali lebih pendek dari tipe sinonimnya, tetapi itu bukan satu-
satunya tujuan dari tipe sinonim: tipe sinonim meningkatkan kemudahan membaca sebuah
program dengan menjadi lebih mnemonik. Bahkan tipe polimorfis sekalipun dapat diberi nama
baru:
type AssocList a b = [(a,b)]
Ini merupakan tipe asosiasi yang mengasosiasikan nilai dari tipe a dengan nilai dari tipe b.
Operator
Untuk tipe data angka, terdapat operator aritmatika untuk penjumlahan, pengurangan, perkalian,
dan pembagian. Dengan operator-operator tersebut, kita bisa menggunakan interpreter Haskell
sebagaimana menggunakan kalkulator. Inilah beberapa contohnya:
? 1 + 1
> 2
? 5 3
> 2
? 7 * 7
> 49
? 4 / 2
> 2.0
? (2 * 2) - (4 * 1 * 3)
> -8
? 3.14 * 2.1 * 2.1
> 13.8474
Dalam contoh-contoh di atas, tanda tanya (?) mengawali input yang kita tuliskan dan tanda lebih
besar (>) mengawali outputnya. Salah satu hal yang menarik adalah bahwa pembagian Integer
dengan Integer menghasilkan bilangan floating point. Hal ini seperti di Pascal, dan berbeda
dengan LISP (nenek moyang Haskell) dan C/C++/C#/Java.
Di Haskell, list merupakan tipe data yang fundamental. Seperti yang telah disebutkan, list hanya
bisa berisi satu tipe data (misalnya integer). Membuat list sangatlah mudah, yaitu dengan syntax
berikut:
? [1, 2, 3, 4]
> [1,2,3,4]
? [a, b, c]
> [1,2,3,4]
? [3.14, 2.72]
> [3.14,2.72]
? []
> []
Kita bisa membuat deret aritmatika dengan mudah, misalnya:
? [1..10]
> [1,2,3,4,5,6,7,8,9,10]
? [2,4..20]
> [2,4,6,8,10,12,14,16,18,20]
Kita juga dapat membuat deret tak hingga misalnya [1,3,..], tetapi kalau itu kita lakukan maka
interpreter tidak akan pernah selesesai menuliskan anggota deret tersebut (kecuali kalau kita
hentikan).
Salah satu operasi list yang sederhana adalah penjumlahan list, contohnya:
? [1..10] ++ [21..30]
> [1,2,3,4,5,6,7,8,9,10,21,22,23,24,25,26,27,28,29,30]
Fungsi
Fungsi dapat dibuat dengan menuliskan tipenya, lalu mendefinisikannya. Misalnya:
tambahSatu :: Integer -> Integer
tambahSatu x = x + 1
Jika kita gunakan fungsinya, inilah outputnya:
? tambahSatu 5
> 6
? tambahSatu 999
> 1000
Baris yang pertama:
tambahSatu :: Integer -> Integer
berarti bahwa fungsi yang kita buat bernama tambahSatu, lalu dia menerima input sebuat
Integer dan akan mengembalikan nilai Integer. Sebetulnya jika kita tidak menentukan tipe
fungsi, interpreter akan menganalisa fungsinya untuk ditentukan tipenya. Walaupun begitu akan
lebih jelas bagi pembaca (dan juga meringankan tuga interpreter) jika tipe fungsinya dituliskan
oleh kita.
Kita bisa lihat bahwa definisi fungsinya sangat mirip dengan notasi matematika untuk fungsi
yang sama, yaitu f(x) = x + 1.
Catatan: Dalam interpreter Hugs 98, definisi fungsi harus berada di sebuah file teks dan untuk
menggunakannya kita harus meload file yang bersangkutan dari Hugs 98. File harus diberi
awalan yang menunjukkan nama modul.
Sebagai contoh, untuk menggunakan fungsi tambahSatu kita harus memiliki file teks dengan isi
sebagai berikut:
module Test where
tambahSatu :: Integer -> Integer
tambahSatu x = x + 1
Jika fungsi tersebut kita save sebagai Test.hs di folder d:\Haskell, maka untuk meloadnya dari
Hugs 98 kita harus memasukkan perintah berikut:
? :load "D:\Haskell\Test.hs"
Jika filenya kita ubah lalu kita ingin meload ulang dari Hugs 98, maka kita cukup mengetikkan
perintah berikut:
? :reload
Grafik
Dalam Haskell, kita juga dapat menggambarkan objek 3 dimensi. Penggambaran objek 3 dimensi
ini dimungkinkan karena dalam Haskell kita dapat menuliskan pemrograman