Kirjoittaja: map_ (24.12.2009)
80-luvulla oltiin niin innostuneita funktionaalisista ohjelmointikielistä, että niitä oli käytössä kymmenittäin. Lopulta päätettiin luoda yksi yhteinen kieli, joka soveltuisi sellaisenaan käytännön ohjelmointiin, opetuskäyttöön ja ohjelmointikielten tutkimukseen. Noin vuosikymmenen kestäneen prosessin aikana standardoitiin Haskell, josta on sittemmin tullut tunnetuin ja suosituin laiska, puhtaasti funktionaalinen kieli.
Se, että Haskell on "puhtaasti funktionaalinen" tarkoittaa, että kielessä ei koskaan ylikirjoiteta arvoja. Kun muuttuja on kerran luotu, sen arvoa ei voi enää muuttaa. Kielen funktiomäärittelyt muistuttavatkin enemmän yhtälöitä kuin komentosarjoja.
Se, että Haskell on "laiska" tarkoittaa, että Haskellissa mitään arvoa ei lasketa auki ennen kuin sitä tarvitaan. Tämä mahdollistaa mielenkiintoisia ohjelmointimenetelmiä, kuten äärettömien tietorakenteiden määrittelemisen.
Haskellissa on myös hyvin pitkälle kehitetty vahva tyyppijärjestelmä. Kääntäjä osaa päätellä melkein kaikkien muuttujien ja funktiomääritelmien tyypit automaattisesti..
main = print "Haskellpukki!"
fibo = 1 : 1 : zipWith (+) fibo (tail fibo) main = print $ takeWhile (< 100) fibo
Ensimmäinen rivi määrittelee äärettömän listan fibo, joka alkaa arvoilla 1 ja 1 ja jatkuu seuraavasti: otetaan lista fibo sekä lista fibo ilman ensimmäistä alkiotaan, laitetaan nämä kaksi listaa vierekkäin ja summataan vierekkäiset alkiot keskenään:
fibo 1 1 2 3 5 8 13 ... tail fibo 1 2 3 5 8 13 21 ... tulos 2 3 5 8 13 21 34 ...
Tästä ideasta on alempana yksinkertaisempia esimerkkejä.
-- Kertomafunktion määritelmä: kertoma 0 = 1 kertoma n = n * (kertoma (n - 1)) -- Tässä jätettiin kirjoittamatta kertomafunktion tyypi. -- Haskell päätteli tyypiksi (Num t) => t -> t, -- eli "funktio, joka ottaa tyyppiä t olevan parametrin ja palauttaa -- tyyppiä t olevan arvon, missä t on jokin lukutyyppi (int, float, ...). -- Lista rakentuu syntaksilla <listan 1. alkio> : <listan loppuosa>. -- Esimerkiksi [1,2,3] == 1:2:3:[] on eräs lista. -- Tehdään funktio, joka summaa listan alkiot. -- Määrätään funktion tyyppi sellaiseksi, että se ottaa parametriksi -- kokonaislukulistan ja palauttaa kokonaisluvun. summaa :: [Int] -> Int -- Kerrotaan, miten funktio käsittelee tyhjän listan. summaa [] = 0 -- Kerrotaan, miten funktio käsittelee epätyhjän listan, joka alkaa -- alkiolla "x" ja jonka loppuosa on "xs". summaa (x:xs) = x + (summaa xs) -- Määritellään yksinkertainen lista: simppeli = [1, 2, 3] -- Lisätään edellisen listan alkuun 0: simppeli' = 0 : simppeli -- tulos: [0, 1, 2, 3] -- Kasvatetaan kaikkia edellisen listan alkioita yhdellä: simppeli'' = map (+1) simppeli' -- tulos: [1, 2, 3, 4] -- Ylläoleva toimii seuraavasti: -- - (+1) luo funktion, joka kasvattaa argumenttiaan yhdellä. -- Sen tyypiksi tulee Int -> Int. -- - map-funktion tyyppi on (a -> b) -> [a] -> [b]. -- Se siis ottaa (a -> b)-tyyppisen funktion sekä listan a-tyyppisiä -- alkioita ja palauttaa listan b-tyyppisiä alkioita. -- Tästä voi jo arvata, mitä map tekee: se kutsuu annettua funktiota -- jokaiselle annetun listan alkioille. -- Määritellään ääretön lista nollia: nollat = 0 : nollat -- tulos: [0, 0, 0, 0, ...] -- Koska muuttujaa "nollat" ei lasketa auki ennen kuin sitä tarvitaan, -- suoritus ei jää tähän kohtaan jumiin. -- Määritellään ääretön lista ykkösiä: ykköset = map (+1) nollat -- tulos: [1, 1, 1, 1, ...] -- Määritellään lista kaikista luonnollisista luvuista: luonnolliset = 0 : (map (+1) luonnolliset) -- tulos: [0, 1, 2, 3, ...] -- Eräs tapa ajatella tämän laskentaa: -- Mikä on listan 1. alkio? -- Se on 0. -- Mikä on listan 2. alkio? -- Se on listan 1. alkio + 1. -- Listan 1. alkio on jo laskettu, se on 0. -- Mikä on listan 3. alkio? -- Se on listan 2. alkio + 1 -- Listan 2. alkio on jo laskettu, se on 1. -- ... -- Tässä vielä eräs tapa määritellä map omaMap f [] = [] omaMap f (x:xs) = (f x):(omaMap f xs)
Kertomafunktion voi ohjelmoida Haskellissa varsin monella erityisen hienolla tavalla.