Konsep Bahasa Pemrograman 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 dengan“eleminasi kode mati”yang 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 mengenkapsulasi 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 terusmenerus 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
l) && (a[h]>=p)) h=h-1; if (l
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 definisidefinisi 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 c1 menghasilkan atau menurunkan ekspresi atau nilai c2, maka ditulis: c1 c2 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 length []
:: [a] -> Integer = 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 compiletime. 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 satusatunya 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