Funktionaalinen ohjelmointi

Samankaltaiset tiedostot
Haskell ohjelmointikielen tyyppijärjestelmä

ELM GROUP 04. Teemu Laakso Henrik Talarmo

arvostelija OSDA ja UDDI palveluhakemistoina.

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

815338A Ohjelmointikielten periaatteet

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

Tyyppiluokat II konstruktoriluokat, funktionaaliset riippuvuudet. TIES341 Funktio-ohjelmointi 2 Kevät 2006

Selainpelien pelimoottorit

Koka. Ryhmä 11. Juuso Tapaninen, Akseli Karvinen. 1. Taustoja 2. Kielen filosofia ja paradigmat 3. Kielen syntaksia ja vertailua JavaScriptiin Lähteet

Ohjelmoinnin peruskurssien laaja oppimäärä

TIEA341 Funktio-ohjelmointi 1, kevät 2008

TIE Principles of Programming Languages. Seminaariesityksen essee. Ryhmä 18: Heidi Vulli, Joni Heikkilä

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Funktionaalinen ohjelmointi

Clojure, funktionaalinen Lisp murre

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

Ohjelmoinnin peruskurssien laaja oppimäärä

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Ohjelmoinnin perusteet Y Python

Funktio-ohjelmoinnin hyödyntäminen peliohjelmoinnissa

Jatkeet. TIES341 Funktio ohjelmointi 2 Kevät 2006

Ohjelmoinnin peruskurssien laaja oppimäärä

TIEA255 Tietotekniikan teemaseminaari ohjelmointikielet ja kehitysalustat. Antti-Juhani Kaijanaho. 16. helmikuuta 2011

Funktionaalinen ohjelmointi

TIE Principles of Programming Languages CEYLON

Common Lisp Object System

PERL. TIE Principles of Programming Languages. Ryhmä 4: Joonas Lång & Jasmin Laitamäki

Ruby. Tampere University of Technology Department of Pervasive Computing TIE Principles of Programming Languages

TIE PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli

Tutoriaaliläsnäoloista

ITKP102 Ohjelmointi 1 (6 op)

Tietorakenteet ja algoritmit

811120P Diskreetit rakenteet

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

Ohjelmoinnin peruskurssien laaja oppimäärä

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Tietorakenteet ja algoritmit - syksy

11/20: Konepelti auki

ITKP102 Ohjelmointi 1 (6 op)

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

2.4 Normaalimuoto, pohja ja laskentajärjestys 2.4. NORMAALIMUOTO, POHJA JA LASKENTAJÄRJESTYS 13

Taas laskin. TIES341 Funktio ohjelmointi 2 Kevät 2006

JAAKKO RINTA-FILPPULA FUNKTIONAALISEN OHJELMOINNIN HYÖDYT JA HAAS- TEET. Kandidaatintyö

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Laiska laskenta, korekursio ja äärettömyys. TIEA341 Funktio ohjelmointi Syksy 2005

Ohjelmoinnin peruskurssien laaja oppimäärä

Prolog kielenä Periaatteet Yhteenveto. Prolog. Toni ja Laura Fadjukoff. 9. joulukuuta 2010

TIES542 kevät 2009 Tyyppijärjestelmän laajennoksia

Ohjelmoinnin peruskurssien laaja oppimäärä

1. Olio-ohjelmointi 1.1

Tietotekniikan valintakoe

Ohjelmoinnin peruskurssien laaja oppimäärä

Luku 3. Listankäsittelyä. 3.1 Listat

Ohjelmoinnin peruskurssien laaja oppimäärä

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 12. lokakuuta 2016

Ohjelmoinnin perusteet Y Python

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 30. marraskuuta 2015

8. Kieliopit ja kielet

Pro gradu -tutkielma Meteorologia SUOMESSA ESIINTYVIEN LÄMPÖTILAN ÄÄRIARVOJEN MALLINTAMINEN YKSIDIMENSIOISILLA ILMAKEHÄMALLEILLA. Karoliina Ljungberg

Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti

Pythonin alkeet Syksy 2010 Pythonin perusteet: Ohjelmointi, skriptaus ja Python

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmoinnin peruskurssien laaja oppimäärä

815338A Ohjelmointikielten periaatteet

Rekursiolause. Laskennan teorian opintopiiri. Sebastian Björkqvist. 23. helmikuuta Tiivistelmä

Aika/Datum Month and year Kesäkuu 2012

1 Tavoitteet. 2 Periaatteet ja ominaisuudet. 2.1 Tyyppipäättely

Ohjelmoinnin peruskurssien laaja oppimäärä

ja λ 2 = 2x 1r 0 x 2 + 2x 1r 0 x 2

Toistorakenteet C#:ssa ja Haskellissa

Mitä funktionaalinen ohjelmointi on

811120P Diskreetit rakenteet

815338A Ohjelmointikielten periaatteet Harjoitus 4 vastaukset

Erlang. Miika Heinonen ja Lassi Uosukainen (Group 92) TIE Principles of Programming Languages Seminaariessee. Yleistä

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

811120P Diskreetit rakenteet

Lisää pysähtymisaiheisia ongelmia

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 4: Ohjelmointi, skriptaus ja Python

Työn laji Arbetets art Level Aika Datum Month and year Sivumäärä Sidoantal Number of pages

14.1 Rekursio tyypitetyssä lambda-kielessä

3. Muuttujat ja operaatiot 3.1

Funktionaalisten kielten oppimisesta ja valinnasta

Ohjelmoinnin peruskurssien laaja oppimäärä

Rajoittamattomat kieliopit (Unrestricted Grammars)

Ohjelmoinnin perusteet Y Python

Tämän vuoksi kannattaa ottaa käytännöksi aina kirjoittaa uuden funktion tyyppi näkyviin, ennen kuin alkaa sen määritemää kirjoittamaan.

Groovy. Niko Jäntti Jesper Haapalinna Group 31

Ohjelmoinnin perusteet Y Python

D-OHJELMOINTIKIELI. AA-kerho, 33. Antti Uusimäki. Arto Savolainen

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit.

Hohde Consulting 2004

Luku 5. Monadit. 5.1 Siirrännän ongelma

lausekkeiden tapauksessa. Jotkin ohjelmointikielet on määritelty sellaisiksi,

Algoritmit 1. Luento 3 Ti Timo Männikkö

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

ITKP102 Ohjelmointi 1 (6 op)

Ongelma(t): Mikä on Turingin kone? Miten Turingin kone liittyy funktioihin ja algoritmeihin? Miten Turingin kone liittyy tietokoneisiin?

Transkriptio:

hyväksymispäivä arvosana arvostelija Funktionaalinen ohjelmointi Eeva Terkki Helsinki 10.12.2012 HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos

HELSINGIN YLIOPISTO HELSINGFORS UNIVERSITET UNIVERSITY OF HELSINKI Tiedekunta Fakultet Faculty Laitos Institution Department Matemaattis-luonnontieteellinen tiedekunta Tekijä Författare Author Eeva Terkki Työn nimi Arbetets titel Title Tietojenkäsittelytieteen laitos Funktionaalinen ohjelmointi Oppiaine Läroämne Subject Tietojenkäsittelytiede Työn laji Arbetets art Level Aika Datum Month and year Sivumäärä Sidoantal Number of pages LuK-tutkielma 10.12.2012 20 sivua Tiivistelmä Referat Abstract Funktionaalinen ohjelmointi on matemaattisiin funktioihin perustuvaa ohjelmointia. Funktionaalisilla ohjelmointikielillä kirjoitetut ohjelmat rakentuvat funktioista, joita määritellään funktioiden avulla. Funktiot ovat usein rekursiivisesti määriteltyjä, ja funktioiden argumentteina ja arvoina voi olla funktioita. Puhdas funktionaalinen ohjelmointi on sivuvaikutuksetonta. Funktionaaliset ohjelmointikielet voivat olla dynaamisesti tai staattisesti tyypitettyjä ja joko applikatiivisessa järjestyksessä tai normaalijärjestyksessä evaluoituvia. Funktionaaliset ohjelmointikielet perustuvat lambdakalkyylin ideoihin. Esimerkkejä funktionaalisista kielistä ovat muun muassa Lisp, ML ja Haskell. Puhtaiden funktionaalisten ohjelmien etu on siinä, että ne toimivat aina odotetulla tavalla. Puhdas funktionaalinen ohjelmointi soveltuu sivuvaikutuksettomuuden ansiosta hyvin myös rinnakkaisohjelmointiin. Funktionaalisten ohjelmointikielten ongelmiin puolestaan lukeutuvat puutteelliset kehitystyökalut ja olemattomat rajapinnat muihin kieliin. ACM Computing Classification System (CCS): D.1.1 [Applicative (Functional) Programming] D.3.2 [Applicative (functional) languages] Avainsanat Nyckelord Keywords funktionaalinen ohjelmointi, funktionaaliset ohjelmointikielet, funktiot, evaluointi, tyyppijärjestelmät Säilytyspaikka Förvaringsställe Where deposited Muita tietoja övriga uppgifter Additional information

Sisältö ii 1 Johdanto 1 2 Funktionaalisten ohjelmointikielten piirteitä 2 2.1 Sivuvaikutuksettomuus.......................... 2 2.2 Evaluointi................................. 3 2.3 Tyypitys.................................. 5 3 Funktiot 6 3.1 Rekursio.................................. 6 3.2 Listat................................... 8 3.3 Korkeamman kertaluvun funktiot.................... 9 4 Funktionaalisia ohjelmointikieliä 12 4.1 Lisp.................................... 13 4.2 ML..................................... 14 4.3 Haskell................................... 15 5 Funktionaalisen ohjelmoinnin etuja ja ongelmia 16 6 Yhteenveto 17 Lähteet 19

1 Johdanto 1 Funktionaalinen ohjelmointi on ohjelmointiparadigma, jossa funktiot ovat keskeisessä asemassa. Funktioilla tarkoitetaan imperatiivisen ohjelmoinnin yhteydessä yleensä aliohjelmia. Funktionaalisessa ohjelmoinnissa funktio puolestaan tarkoittaa funktiota sanan matemaattisessa merkityksessä [Hin09, s. 87]: funktiot ovat sääntöjä, joiden mukaisesti lähtöjoukon arvot kuvautuvat maalijoukon arvoille. Samoja argumentteja käytettäessä funktion arvo on jokaisella kutsukerralla sama. Funktiot ovat funktionaalisessa ohjelmoinnissa ns. first-class-arvoja (first-class value) [Hud89, s. 382]. First-class-arvo tarkoittaa arvoa esimerkiksi kokonaislukua tai funktiota jonka voi sitoa muuttujaan ja jota voi käyttää parametrina ja paluuarvona. Funktionaalisessa ohjelmoinnissa funktioiden argumentteina ja arvoina voi siis olla funktioita. Funktiota, jonka argumenttina tai arvona on funktio, kutsutaan korkeamman kertaluvun funktioksi (higher order function) [Hud89, s. 382]. Imperatiivisessa ohjelmoinnissa muuttujien arvot tietyllä hetkellä muodostavat ohjelman senhetkisen tilan. Jonkin muuttujan arvon muuttuminen tarkoittaa siis muutosta ohjelman tilassa. Tilan muutosta kutsutaan sivuvaikutukseksi [Hud89, s. 361]. Puhtaassa funktionaalisessa ohjelmoinnissa tilaa ei ole, ja puhtaat funktionaaliset ohjelmointikielet ovat täysin sivuvaikutuksettomia. Funktiokutsun seurauksena ei siis voi tapahtua muuta kuin funktion arvon laskeminen [Hin09, s. 87]. Funktionaalisen ohjelmoinnin juuret ovat lambdakalkyylissa, joka on 1930-luvulla kehitetty matemaattinen laskennan malli [Chu41]. Lambdakalkyylin ideoihin perustuvia funktionaalisia ohjelmointikieliä ovat muun muassa Lisp, ML ja Haskell. Nykyisestä ohjelmistotuotannosta suurin osa tapahtuu edelleen perinteisillä imperatiivisilla ohjelmointikielillä, mutta funktionaaliset kielet ovat kasvattaneet suosiotaan paitsi tieteen myös liike-elämän sovelluksissa [Sco09, s. 505].

2 Funktionaalisten ohjelmointikielten piirteitä 2 Funktionaaliselle ohjelmoinnille ominainen piirre on sivuvaikutuksien välttäminen. Funktionaaliset kielet voivat olla joko täysin sivuvaikutuksettomia, tai ne voivat sallia imperatiivisen ohjelmoinnin piirteitä. Vastaavasti funktionaalisten ohjelmoinnin piirteitä esiintyy myös muita ohjelmointiparadigmoja edustavissa kielissä, kuten Pythonissa ja Rubyssa [Sco09, s. 536]. Tässä luvussa kerrotaan funktionaalisten ohjelmointikielten sivuvaikutuksettomuudesta ja tyyppijärjestelmistä sekä eri tavoista evaluoida funktioita. 2.1 Sivuvaikutuksettomuus Imperatiivisessa ohjelmoinnissa arvon sijoittaminen muuttujaan on paljon käytetty operaatio. Muuttujien arvot muodostavat ohjelman tilan kullakin hetkellä, ja tila muuttuu, kun johonkin muuttujista sidotaan uusi arvo. Tilan muuttumista kutsutaan sivuvaikutukseksi [Hud89, s. 361]. Puhdas funktionaalinen ohjelmointi on sivuvaikutuksetonta [Hin09, s. 87]. Siinä ei esiinny muuttujia, joiden arvoja voisi muuttaa, eikä muuttuvia tilojakaan siis ole. Muuttujaan voi sitoa arvon vain kerran, eikä sitä sen jälkeen voi enää muuttaa [Hug89, s. 98]. Kun funktiota kutsutaan, kutsusta voi seurata ainoastaan funktion arvon laskeminen ja lasketun arvon palauttaminen. Kun sivuvaikutuksia ei ole, voidaan olla varmoja siitä, että ohjelma toimii aina tietyllä tavalla [Sco09, s. 509]. Tietokoneohjelmiin liittyy kuitenkin paljon toimintoja, joiden yhteydessä sivuvaikutuksilta ei voida välttyä. Muun muassa syötteen lukeminen on toiminto, jota tarvitaan useissa ohjelmissa ja joka aiheuttaa luonnostaan sivuvaikutuksia [Sco09, s. 525]. Myös poikkeuksien yhteydessä esiintyy sivuvaikutuksia [Wad92, s. 1]. Eri funktionaaliset ohjelmointikielet käsittelevät sivuvaikutuksia

3 aiheuttavia toimintoja eri keinoin. Suurin osa funktionaalisista ohjelmointikielistä sallii imperatiivisen ohjelmoinnin piirteitä, kuten uusien arvojen sitomisen muuttujiin [Hin09, s. 88]. Näillä kielillä sivuvaikutuksia aiheuttavat toiminnot voidaan toteuttaa vastaavalla tavalla kuin imperatiivisilla ohjelmointikielillä. Tällöin ohjelmoija joutuu ohjelmoidessaan ottamaan huomioon sivuvaikutusten mahdollisesti aiheuttamat ongelmat. Imperatiivisten ohjelmointikielten piirteitä sallivia funktionaalisia ohjelmointikieliä ovat muun muassa Lisp [Hud89, s. 369] ja ML [Hud89, s. 374]. Funktionaalisen ohjelmointikielen sanotaan olevan puhdas (pure), jos siinä ei esiinny lainkaan sivuvaikutuksia. Myös puhtaalla funktionaalisella ohjelmointikielellä voidaan toteuttaa sivuvaikutuksia aiheuttavia toimintoja. Tällöin sivuvaikutukset sallitaan vain erityisissä ohjelmointikielen rakenteissa [Hin09, s. 88]. Esimerkiksi Haskell on puhdas funktionaalinen kieli [Sco09, s. 507]. 2.2 Evaluointi Jotta funktion arvo voidaan laskea, täytyy laskemisessa tarvittavat argumentit evaluoida, eli lausekkeiden arvot on laskettava ja funktiokutsut suoritettava. Argumentit voidaan evaluoida joko applikatiivisessa järjestyksessä (applicative-order) tai normaalijärjestyksessä (normal-order) [ASS96, s. 367]. Funktionaalisissa ohjelmointikielissä esiintyy kumpaakin evaluointitapaa. Applikatiivinen järjestys tarkoittaa, että kaikki funktion argumentit evaluoidaan täysin ennen kuin funktion arvoa aletaan laskea [ASS96, s. 367]. Applikatiivinen järjestys on käytössä esimerkiksi Lisp- ja ML-kielissä [Hud89, s. 384]. Seuraavassa esimerkissä nähdään, miten evaluointi tapahtuu applikatiivisessa järjestyksessä. Haskell-kielen syntaksilla kirjoitettu nelio-funktio laskee argumenttina saamansa luvun neliön:

4 nelio x = x * x Jos nelio-funktion arvo argumentilla (1 + 1) laskettaisiin käyttämällä evaluoinnissa applikatiivista järjestystä, evaluointi etenisi seuraavalla tavalla: nelio (1 + 1) => nelio 2 => 2 * 2 => 4 Ensin evaluoidaan funktion argumentti, minkä jälkeen funktion arvo lasketaan evaluoidun argumentin avulla. Normaalijärjestyksessä tapahtuvassa evaluoinnissa funktion argumentit evaluoidaan vasta silloin, kun argumenttien arvoja tarvitaan funktion arvon laskemisessa [ASS96, s. 367]. Argumentti jätetään kokonaan evaluoimatta, jos funktio ei käytä sitä mihinkään. Tällöin vältytään evaluoimasta mitään ylimääräistä, ja jos tarpeetonta evaluoitavaa on paljon, on evaluointi normaalijärjestyksessä tehokkaampaa kuin applikatiivisessa järjestyksessä [Hud89, s. 384]. Normaalijärjestyksessä evaluoimalla vältytään myös virhetilanteilta tapauksissa, joissa osa koodia sisältää virheitä, mutta kyseistä osaa ei koskaan tarvita funktion arvon laskemisessa [ASS96, s. 367]. Applikatiivisessa järjestyksessä evaluoitaessa myös tarpeeton, virheellinen osa evaluoitaisiin, jolloin kyseisen ohjelman suoritus päätyisi virhetilanteeseen. Yksinkertaisesti toteutettuna normaalijärjestyksessä evaluoiminen on kuitenkin monissa tapauksissa tehotonta verrattuna applikatiiviseen järjestykseen [Hud89, s. 383]. Sama lauseke nimittäin joudutaan evaluoimaan yhtä monta kertaa kuin sen arvoa käytetään funktion arvon laskemisessa. Yllä määritellyn nelio-funktion arvo argumentilla (1 + 1) saataisiin normaalijärjestyksessä evaluoimalla seuraavien vaiheiden kautta:

5 nelio (1 + 1) => (1 + 1) * (1 + 1) => 2 * (1 + 1) => 2 * 2 => 4 Argumentti (1 + 1) joudutaan evaluoimaan kahdesti, kun applikatiivisessa järjestyksessä evaluoitaessa sama argumentti oli tarpeen evaluoida vain kerran. Normaalijärjestyksessä evaluoinnin voi toteuttaa tehokkaammin käyttämällä laiskaa evaluointia (lazy evaluation) [Hud89, s. 384]. Laiskassa evaluoinnissa argumentit evaluoidaan vasta tarvittaessa, ja lisäksi kunkin argumentin evaluointi tapahtuu korkeintaan kerran [Hud89, s. 384-385]. Laiska evaluointitapa mahdollistaa äärettömän pituisten tietorakenteiden käsittelemisen [Sco09, s. 525]. Esimerkki laiskasti evaluoituvasta ohjelmointikielestä on Haskell, ja luvussa 3.2 nähdään, miten Haskell-kielellä voidaan käsitellä äärettömän pituisia listoja. 2.3 Tyypitys Funktionaalisten ohjelmointikielten joukkoon mahtuu sekä dynaamisesti että staattisesti tyypitettyjä kieliä. Dynaamisessa tyypityksessä ohjelman muuttujien ja funktioiden tyypit tarkistetaan suorituksen aikana [LTh09, s. 68]. Tällöin niiden tyyppejä ei tarvitse erikseen määritellä ohjelmakoodissa, mutta jos koodi sisältää tyyppivirheen, voi ohjelma kaatua kesken suorituksen. Staattisessa tyypityksessä tyypit tarkistetaan ohjelman kääntövaiheessa [LTh09, s. 68]. Tyyppivirheen jäädessä kääntäjän haaviin ohjelma ei käänny. Tämä varmistaa, että suorituksen aikana tyyppivirheitä ei esiinny. Staattisesti tyypitetyn ohjelman suorittaminen on nopeampaa kuin dynaamisesti tyypitetyn, sillä suorituksen aikana

6 ei tarvitse enää tehdä tyyppien tarkistuksia [LTh09, s. 70]. Staattisesti tyypitettyä ohjelmointikieltä käytettäessä ohjelman elementtien tyypit täytyy määritellä ohjelmakoodissa, paitsi jos käytettävä ohjelmointikieli käyttää tyypinpäättelyä (type inference) [LTh09, s. 70]. Tyypinpäättelyn ansiosta ohjelmoijan ei tarvitse kirjoittaa jokaisen muuttujan ja lausekkeen tyyppejä koodiin. Kielen kääntäjä päättelee oikeat tyypit koodin rakenteen ja siinä esiintyvien operaatioiden ja arvojen perusteella [Sco09, s. 316]. Siten se osaa odottaa oikean tyyppisiä arvoja kunkin funktion argumenteiksi ja arvoiksi, vaikka niitä ei olisi erikseen määritelty koodissa. 3 Funktiot Matemaattinen funktio on sääntö, jonka mukaisesti jokainen lähtöjoukon arvo kuvautuu jollekin maalijoukon arvolle. Samoilla argumenteilla funktio saa aina saman arvon [Hin09, s. 87]. Funktionaalisella ohjelmointikielellä kirjoitettu ohjelma koostuu funktioista: toisiinsa liitetyt yksinkertaiset funktiot muodostavat yhdessä monimutkaisempia kokonaisuuksia. Tämän luvun esimerkkifunktiot on kirjoitettu Haskellkielellä. 3.1 Rekursio Funktionaalinen ohjelmointi on pääosin sivuvaikutuksetonta, eikä siinä esiinny muuttujiin sijoittamista. Siksi funktionaalisessa ohjelmoinnissa ei voida käyttää imperatiiviselle ohjelmoinnille tyypillisiä toistorakenteita, kun jokin koodin osa halutaan toistaa monta kertaa [Hin09, s. 87]. Funktionaalisissa ohjelmointikielissä toisto toteutetaan käyttämällä rekursiota, sillä se on ainoa sivuvaikutukseton tapa suorittaa jokin toiminto monta kertaa [Sco09, s. 508].

7 Imperatiivisten kielten toistorakenteissa tilaa tyypillisesti muutetaan jokaisessa iteraatiossa, eli jokaisella toistokerralla yhden tai useamman muuttujan arvo muuttuu [Hin09, s. 87-88]. Rekursiivisessa toistossa jokaisessa funktiokutsussa yhdellä tai useammalla argumentilla on eri arvo kuin edellisessä funktiokutsussa. Rekursiivisesti määritelty funktio kutsuu itseään, kunnes jokin argumenteista täyttää tietyn ehdon ja rekursio päättyy. Seuraava funktio laskee n:nnen Fibonaccin luvun käyttämällä rekursiota: fibonacci 1 = 1 fibonacci 2 = 1 fibonacci n = fibonacci(n - 1) + fibonacci(n - 2) Funktiolla on kolmeen osaan jaettu määritelmä, jossa käytetään hahmonsovitusta (pattern matching). Hahmonsovituksessa funktion saamaa argumenttia verrataan eri argumenttivaihtoehtoihin [Hud89, s. 388]. Argumenttia sovitetaan järjestyksessä eri vaihtoehtoihin alkaen ylimmästä, ja argumentin mukaan määräytyy, mitä funktio tekee. Jos fibonacci-funktion argumenttina oleva luku on 1, lasketaan määritelmän ensimmäinen rivi, ja jos argumentti on 2, lasketaan toinen rivi. Molemmissa tapauksissa funktion arvoksi tulee luku 1. Jos argumenttina on jokin muu luku, lasketaan funktion määritelmän alin rivi, eli lasketaan yhteen kaksi edellistä Fibonaccin lukua. Kaksi edellistä Fibonaccin lukua saadaan kutsumalla fibonacci-funktiota argumenteilla (n - 1) ja (n - 2). Esimerkiksi neljännen Fibonaccin luvun laskeminen fibonacci-funktiolla tapahtuu seuraavien välivaiheiden kautta: fibonacci 4 fibonacci (4-1) + fibonacci (4-2) fibonacci 3 + fibonacci 2

8 (fibonacci (3-1) + fibonacci (3-2)) + fibonacci 2 (fibonacci 2 + fibonacci 1) + fibonacci 2 (1 + 1) + 1 3 3.2 Listat Listat ovat tärkeä tietorakenne funktionaalisessa ohjelmoinnissa, ja rekursio on hyödyllinen väline niiden läpikäymiseen [Sco09, s. 508]. Kun funktio käy listan rekursiivisesti läpi, se käsittelee listan yksi alkio kerrallaan. Tyypillisesti funktion argumenttina olevan listan ensimmäinen alkio käsitellään, ja funktio kutsuu itseään listan jäljelle jäävälle osalle. Sama toistuu, kunnes funktion argumenttina on tyhjä lista, eli lista on käyty kokonaan läpi. Seuraava funktio laskee äärellisen pituisen listan alkioiden summan rekursiivisesti: summa [] = 0 summa (x:xs) = x + summa xs Funktion määritelmässä tehdään hahmonsovitus listalle. Merkintä [] tarkoittaa tyhjää listaa ja merkintä (x:xs) vähintään yhden alkion sisältävää listaa. Jälkimmäisessä x on listan ensimmäinen alkio ja xs lopuista alkioista koostuva lista, joka voi olla myös tyhjä lista. Jos summa-funktion argumenttina on tyhjä lista, niin funktion arvo on nolla. Jos lista ei ole tyhjä, siirrytään hahmonsovituksessa seuraavaan funktion määritelmään, jolloin funktion arvo määräytyy summa-funktion määritelmän toisen rivin mukaisesti. Tällöin lasketaan yhteen listan ensimmäisen alkion arvo ja listan muiden alkioiden summa, joka saadaan laskettua kutsumalla summa-funktiota lopulle listasta. Funktion kutsumista argumenttina saadun listan loppuosalle jatketaan, kunnes argumen-

9 tiksi lopulta annetaan tyhjä lista ja rekursio päättyy. Laiskasti evaluoituvilla funktionaalisilla kielillä, kuten Haskellilla, on mahdollista käsitellä päättymättömiä tietorakenteita, kuten äärettömän pituisia listoja [Sco09, 525]. Äärettömän pituisia listoja voidaan hyödyntää esimerkiksi n ensimmäisen luonnollisen luvun yhteen laskemisessa aiemman esimerkin summa-funktion avulla: nsumma n = summa (take n [1..]) Funktiolla nsumma on yksi argumentti, joka kertoo, monenko ensimmäisen luonnollisen luvun summa halutaan laskea. Funktio kutsuu summa-funktiota, jonka argumentti on take-nimisen funktion kutsusta saatava lista. Funktio take on määritelty Haskellin standardikirjastossa [Ghc12b]. Sen argumentteina ovat kokonaisluku ja lista. Funktion take arvo on lista, joka koostuu halutusta määrästä alkioita argumenttina saadun listan alusta. Merkintä [1..] tarkoittaa ääretöntä luonnollisten lukujen listaa eli listaa [1,2,3,4,..]. Funktion summa argumentti on siis tässä tapauksessa lista, joka koostuu n ensimmäisestä luonnollisesta luvusta. Esimerkiksi funktiokutsulla nsumma 1000 funktion nsumma arvo olisi 500500. 3.3 Korkeamman kertaluvun funktiot Funktionaalisessa ohjelmoinnissa funktiot ovat first-class-arvoja, eli funktion voi sitoa muuttujaan, ja sitä voi käyttää argumenttina ja arvona [Hud89, s. 382]. Funktiota, jonka argumenttina tai arvona on funktio, kutsutaan korkeamman kertaluvun funktioksi. Funktionaalisessa ohjelmoinnissa funktiolla voi siis olla funktio argumenttina tai arvona, ja toisaalta kyseinen funktio voi myös itse olla funktion argumentti tai arvo. Korkeamman kertaluvun funktioilla voidaan toteuttaa yleiskäyttöisiä funktioita. Niiden avulla voidaan ilmaista yleisessä muodossa sellaisia funktioita, joiden määritel-

10 mien rakenne on samanlainen [ASS96, s. 68]. Kun useamman funktion voi ilmaista yleisesti, ei kaikille tarvitse kirjoittaa määritelmää erikseen. Seuraavaksi tarkastellaan kahta funktiota, jotka voitaisiin ilmaista saman korkeamman kertaluvun funktion avulla. Funktio neliot luo listan, joka koostuu sen argumenttina saaman listan alkioiden neliöistä: neliot [] = [] neliot (x:xs) = x ^ 2 : neliot xs Funktiossa käytetään hahmonsovitusta. Jos argumenttina on tyhjä lista, funktion arvo on tyhjä lista. Jos argumentti on ainakin yhden alkion sisältävä lista, funktion arvo määräytyy funktion määritelmän toisen rivin mukaisesti. Lausekkeessa x ^ 2 : neliot xs esiintyvä operaattori : muodostaa listan, jossa operaattorin vasemmalla puolella oleva alkio lisätään operaattorin oikealla puolella olevan listan ensimmäiseksi alkioksi. Listan ensimmäiseksi alkioksi tulee siis argumenttina saadun listan ensimmäisen alkion neliö, ja loput alkiot saadaan kutsumalla neliot-funktiota rekursiivisesti lista-argumentin loppuosalle. Esimerkiksi funktiokutsu neliot [1,2,3] tuottaa listan [1,4,9]. Toinen esimerkkifunktio isoiksi saa argumenttina merkkijonon, ja sen arvo on vastaava merkkijono, jossa kirjaimet on muutettu isoiksi. Merkkijonot ovat Haskellissa Char-tyyppisistä arvoista koostuvia listoja, ja merkkijonojen kirjaimia voi käsitellä samalla tavalla kuin muidenkin listojen alkioita. Funktiossa käytetään Haskellin standardikirjaston funktiota toupper, joka palauttaa argumenttina saamaansa kirjainta vastaavan ison kirjaimen [Ghc12a]. isoiksi "" = "" isoiksi (c:cs) = toupper c : isoiksi cs Jos isoiksi-funktion argumenttina saama merkkijono on tyhjä, funktion arvo on

11 tyhjä merkkijono. Muussa tapauksessa rakennetaan uusi lista kutsumalla funktiota toupper merkkijonon ensimmäiselle merkille ja isoiksi-funktiota rekursiivisesti merkkijonon loppuosalle. Esimerkiksi funktiokutsu isoiksi "Hei maailma" tuottaisi merkkijonon "HEI MAAILMA". Funktioiden neliot ja isoiksi määritelmät muistuttavat suurelta osin toisiaan. Funktionaalisiset ohjelmointikielet tarjoavat niiden kaltaisten operaatioiden toteutukseen korkeamman asteen funktion, joka Haskellissa tunnetaan nimellä map. Funktio saa argumentteina funktion ja listan, ja se käyttää argumenttina saamaansa funktiota jokaiseen listan alkioon. Arvo, jonka map-funktio palauttaa, on lista argumenttina saadun funktion tuottamista arvoista. Funktio map on toteutettu Haskellin standardikirjastossa seuraavanlaisesti [Ghc12c]: map _ [] = [] map f (x:xs) = f x : map f xs Funktiossa käytetään hahmonsovitusta lista-argumenttiin. Jos map-funktiota kutsuttaessa toisena argumenttina on tyhjä lista, funktio palauttaa tyhjän listan. Merkintää _ voi käyttää argumentin tilalla, kun argumenttia ei tarvita. Jos map-funktion toinen argumentti on vähintään yhden alkion pituinen lista, suoritetaan funktion määritelmän toinen rivi. Tällöin palautetaan uusi lista, jonka ensimmäinen alkio on arvo, jonka funktio f palauttaa argumentilla x. Listan loppuosa on lista, jonka map-funktio palauttaa, kun sitä kutsutaan ensimmäisenä argumenttina funktio f ja toisena argumenttina alkuperäisen listan loppuosa xs. Funktiota map käyttäen voidaan listan lukujen neliöiden laskeminen nyt ilmaista neliot-funktiota lyhyemmin: map (\x -> x ^ 2) [1,2,3]

12 Funktiokutsun ensimmäinen argumentti on anonyymi funktio, joka saa yhden argumentin ja palauttaa kyseisen argumentin neliön. Funktio map käyttää anonyymia funktiota kaikkiin toisen argumenttinsa alkioihin. Tällöin map-funktion arvoksi tulee sama arvo kuin nelio-funktion arvo oli samalla lista-argumentilla, eli lista [1,4,9]. Vastaavasti funktion isoiksi sijaan voidaan kirjoittaa: map toupper "Hei maailma" Funktio map käyttää toupper-funktiota merkkijonon jokaiseen kirjaimeen. Tällöin map saa saman arvon kuin aiemmin esitelty isoiksi-esimerkki: "HEI MAAILMA". 4 Funktionaalisia ohjelmointikieliä Funktionaaliset ohjelmointikielet perustuvat lambdakalkyyliin, joka on Alonzo Churchin 1930-luvulla kehittämä matemaattinen laskennan malli [Chu41]. Sitä pidetään ensimmäisenä funktionaalisena ohjelmointikielenä, vaikka sen kehittämisen aikaan nykyaikaisia tietokoneita ei ollut vielä olemassa [Hud89, s. 363]. Lambdakalkyyli vastaa laskentavoimaltaan Turingin konetta: kaikki Turingin koneella laskettavissa olevat asiat pystytään laskemaan myös lambdakalkyylilla [Sco09, s. 506]. Toisin sanoen lambdakalkyyli on Turing-täydellinen. Lambdakalkyylin syntaksissa merkki λ esittelee funktion argumentit. Argumenttien perässä olevan pisteen jälkeen on lauseke, jolla funktion arvo lasketaan. Esimerkiksi kaksi lukua yhteen laskeva funktio voidaan kirjoittaa lambdakalkyylin syntaksilla seuraavasti: λxy.x + y Tässä luvussa esitellään kolmea erilaista lambdakalkyylin ideoihin perustuvaa funktionaalista ohjelmointikieltä.

13 4.1 Lisp Lisp on John McCarthyn 1950-luvulla kehittämä funktionaalinen ohjelmointikieli [McC60], ja se kuuluu vanhimpiin edelleen käytössä oleviin ohjelmointikieliin. Lispkieli esitteli aikoinaan uusia ominaisuuksia, jotka ovat olleet merkittävä osa sen jälkeen kehitettyjä funktionaalisia kieliä, mutta myös muita ohjelmointiparadigmoja edustavia kieliä. Esimerkiksi ehtolauseiden käyttö sekä roskienkeruu esiintyivät ensimmäisen kerran Lispissä [Hud89, s. 368]. Lisp ei ole puhdas funktionaalinen ohjelmointikieli, sillä se sallii imperatiivisen ohjelmoinnin piirteitä [Hud89, s. 369]. Lisp on dynaamisesti tyypitetty [LTh09, s. 68], ja se evaluoituu applikatiivisessa järjestyksessä [Hud89, s. 384]. Vuosien varrella Lispkielestä on kehitetty useita murteita, ja nykyään Lisp on murteistaan koostuva funktionaalisten kielten perhe [Hin09, s. 89]. Lispin murteita ovat muun muassa Common Lisp, Scheme ja Clojure. Lisp-kieltä on käytetty erityisesti tekoälyyn liittyvissä sovelluksissa [Sco09, s. 505]. Nimi Lisp on lyhenne sanoista "LISt Processor"[McC60, s. 184]. Lisp-kielissä funktiot esitetään listoina, joiden ensimmäinen alkio on käytettävän funktion nimi ja loput alkiot ovat funktion argumentteja. Myös Lisp-ohjelmat itsessään ovat listamuodossa, ja siten niitä voidaan käsitellä samalla tavalla kuin muutakin dataa [Sco09, s. 517]. Seuraavassa esimerkissä on Common Lisp -kielellä toteutettu yksinkertainen rekursiivinen funktio n:nnen Fibonaccin luvun laskemiseen: (defun fibonacci (n) (if (< n 3) 1 (+ (fibonacci (- n 1)) (fibonacci (- n 2)))))

14 Funktio defun määrittelee fibonacci-nimisen funktion, jolla on yksi argumentti. Funktion fibonacci määritelmässä käytetään ehtolausetta: jos funktiokutsussa saadun argumentin arvo on pienempi kuin kolme, fibonacci-funktion arvoksi tulee luku 1. Muussa tapauksessa kutsutaan funktiota +, joka laskee argumenttiensa summan. Argumentteina on kaksi fibonacci-funktion kutsua; toinen lukua n yhtä pienemmälle ja toinen kahta pienemmälle luvulle. 4.2 ML ML on Lispin tavoin murteistaan koostuva funktionaalisten kielten perhe, jonka jäseniä ovat muun muassa Standard ML, OCaml ja F# [Hin09, s. 89]. ML-ohjelmointikieli kehitettiin 1970-luvulla, ja nimi ML tulee sanasta "meta language"[mmh97, s. ix]. Kieli oli alunperin metakieli, jota käytettiin toisen kielen analysointiin. ML:n kehittäjät huomasivat kielen hyödyllisyyden ja ryhtyivät kehittämään sitä lisää [Hud89, s. 374]. ML-kielet ovat staattisesti tyypitettyjä, ja niissä on käytössä tyypinpäättely [Sco09, s. 508]. ML oli ensimmäinen ohjelmointikieli, jossa tyypinpäättelyä käytettiin osana kieltä [Hud89, s. 375]. Sen jälkeen ilmestyneet staattisesti tyypitetyt funktionaaliset ohjelmointikielet ovat myös omaksuneet tyypinpäättelyn. Lispin tavoin ML ei ole puhdas funktionaalinen kieli, ja sillä on mahdollista ohjelmoida myös imperatiivisella tyylillä [Hud89, s. 374]. Seuraavassa esimerkissä on n:nnen Fibonaccin luvun laskeva funktio toteutettuna Standard ML -kielellä: fun fibonacci 1 = 1 fibonacci 2 = 1 fibonacci n = fibonacci (n - 1) + fibonacci (n - 2);

15 Avainsana fun määrittee fibonacci-nimisen funktion, jolla on yksi argumentti. Funktion toteutuksessa käytetään hahmonsovitusta, ja merkki erottaa eri vaihtoehdot toisistaan. Funktion määritelmä muistuttaa luvussa 3.1 esiteltyä Haskellkielellä kirjoitettua fibonacci-funktion määritelmää, ja funktio myös toimii samalla tavalla. 4.3 Haskell Haskell on puhdas ja laiskasti evaluoituva funktionaalinen ohjelmointikieli. ML:n tavoin Haskell on staattisesti tyypitetty, ja siinä on myös käytössä sama tyypinpäättelyjärjestelmä kuin ML-kielessä [Hud89, s. 375]. Haskell on enimmäkseen opetuksessa käytetty kieli [HHP07, s. 40], ja se on antanut vaikutteita moniin muihin ohjelmointikieliin, joihin lukeutuvat muun muassa Python ja Scala [HHP07, s. 45-46]. Haskell-kielen kehitti sitä varten perustettu komitea 1980-luvulla [HHP07, s. 3-4]. Ennen Haskellin ilmestymistä eri tahot olivat kehittäneet useita eri puhtaita, laiskoja funktionaalisia kieliä [HHP07, s. 3]. Nämä kielet muistuttivat paljon toisiaan, mutta ne eivät olleet kovin laajassa käytössä. Haskell sai alkunsa tarpeesta kehittää yksi yleinen funktionaalinen ohjelmointikieli, joka olisi puhdas ja laiska. Kuten luvussa 3.1 nähtiin, yksinkertainen n:nnen Fibonaccin luvun laskeva funktio voidaan toteuttaa Haskellilla seuraavanlaisesti: fibonacci :: Int -> Int fibonacci 1 = 1 fibonacci 2 = 1 fibonacci n = fibonacci(n - 1) + fibonacci(n - 2) Funktion toiminta on selitetty luvussa 3.1, mutta kyseisen luvun esimerkistä poiketen funktion määritelmän alkuun on lisätty tyyppiannotaatio (type signature).

16 Tyyppiannotaatio fibonacci :: Int -> Int tarkoittaa, että funktio fibonacci ottaa yhden Int-tyyppisen argumentin ja palauttaa Int-tyyppisen arvon. Koska Haskell käyttää tyypinpäättelyä, tyyppiannotaatio ei ole funktion määrittelyssä välttämätön, ja sen voisi jättää myös kokonaan kirjoittamatta. 5 Funktionaalisen ohjelmoinnin etuja ja ongelmia Funktionaalisesta ohjelmoinnista on etua ohjelmistojen kehittämisessä. Puhtaasti funktionaalisten ohjelmien tiedetään käyttäytyvän aina tietyllä tavalla, joten yllättäviä sivuvaikutuksia ei voi tapahtua ohjelman suorituksen aikana [Sco09, s. 509]. Tämä tekee funktionaalisista ohjelmista vakaita ja luotettavia. Sivuvaikutuksettomuuden vuoksi funktionaalisten ohjelmien on koettu useissa käytännön tapauksissa olevan vastaavaa imperatiivista ohjelmaa helpompia kehittää ja ylläpitää [Sco09, s. 509]. Sivuvaikutuksettomuudesta nähdään olevan hyötyä myös rinnakkaisen laskennan toteuttamisessa [Hin09, s. 90]. Rinnakkaista ja hajautettua laskentaa tarvitaan, jotta tietokoneohjelmien suorituksen nopeutta voidaan nostaa. Rinnakkaisohjelmoinnin ongelma on tiedon pitäminen eheänä, kun useampi säie pyrkii käsittelemään sitä samanaikaisesti [Hin09, s. 89]. Puhtaassa funktionaalisessa ohjelmakoodissa ei ole muuttujia, joten rinnakkaisuuden toteutuksessa ei ole vaaraa siitä, että säikeet lukisivat muuttujia tai kirjoittaisivat niihin väärään aikaan [Hin09, s. 90]. Funktionaalisilla ohjelmointikielillä voidaan siis toteuttaa rinnakkaista laskentaa turvallisesti. Automaattisen rinnakkaistamisen tarpeisiin funktionaalisten ohjelmointikielten kääntäjät eivät kuitenkaan vielä ole kyllin kehittyneitä [Hin09, s. 90]. Eduistaan huolimatta funktionaalinen ohjelmointi ei ole yhtä suuressa suosiossa kuin perinteinen imperatiivinen ohjelmointi [Sco09, s. 505]. Osaksi funktionaalisten kielten laajempaa käyttöä rajoittaa se, että rajapintoja muihin ohjelmointikieliin on

17 vähän [Sco09, s. 509]. Lisäksi puutteita on ohjelmistojen kehityksessä tarvittavissa työkaluissa [Wad98, s. 24]. Virheiden etsimiseen ja ajan- ja tilankäytön profilointiin tarvittavia välineitä ei ole useille funktionaalisille ohjelmointikielille saatavilla, eikä funktionaalisille kielille tarkoitettuja monipuolisia ohjelmointiympäristöjä juurikaan ole. Imperatiiviseen ohjelmointiin tottuneelle ohjelmoijalle funktionaaliseen ohjelmointiin siirtyminen ei ole välttämättä helppoa [Wad98, s. 24]. Koska funktionaalinen ohjelmointityyli poikkeaa paljon imperatiivisesta tyylistä, voi funktionaalisen ratkaisun keksiminen ongelmaan olla vaikeaa siinä missä imperatiivinen ratkaisu tulee mieleen luonnostaan. Lisäksi jotkut ongelmat ovat helpommin ratkaistavissa imperatiivisella ohjelmointityylillä kuin funktionaalisesti [Wad98, s. 24]. 6 Yhteenveto Funktionaalinen ohjelmointi on matemaattisten funktioiden käyttöön perustuvaa ohjelmointia. Funktionaalisen ohjelmoinnin juuret ovat lambdakalkyylissa, ja esimerkkejä funktionaalisista ohjelmointikielistä ovat muun muassa Lispin ja ML:n murteet sekä Haskell. Funktionaalisessa ohjelmoinnissa funktiot ovat usein rekursiivisesti määriteltyjä. Lisäksi funktionaaliselle ohjelmoinnille on ominaista funktioiden esiintyminen funktioiden argumentteina ja arvoina. Tämä mahdollistaa yleiskäyttöisten funktioiden laatimisen. Funktionaaliset kielet voivat olla staattisesti tai dynaamisesti tyypitettyjä. Staattisesti tyypitetyissä kielissä käytetään usein tyypinpäättelyä. Funktioiden evaluointi voidaan suorittaa applikatiivisessa järjestyksessä tai normaalijärjestyksessä. Jälkimmäisen vaihtoehdon tapauksessa laiska evaluointi on tehokas tapa evaluoida funk-

18 tiot. Funktionaalisessa ohjelmoinnissa pyritään välttämään sivuvaikutuksia: funktio ei tee muuta kuin laskee arvon ja palauttaa sen. Luonnostaan sivuvaikutuksia aiheuttavat toiminnot, kuten syötteen lukeminen ja poikkeukset, voidaan toteuttaa funktionaalisissa ohjelmointikielissä joko kieleen rakennettuna mekanismina tai sallimalla imperatiivisen ohjelmointityylin käyttöä. Sivuvaikutuksettomuudesta on etua, kun halutaan varmistua siitä, että ohjelma toimii kaikissa tilanteissa halutulla tavalla. Lisäksi sivuvaikutuksettomuudesta voidaan hyötyä rinnakkaisessa laskennassa. Funktionaalinen ohjelmointi ei ole yhtä laajassa käytössä kuin imperatiivinen ohjelmointi. Osaksi tätä selittävät puutteet kehitystyökaluissa sekä rajapinnoissa muihin ohjelmointikieliin. Lisäksi imperatiiviseen ohjelmointiin tottuneelle funktionaalisen ohjelmoinnin omaksuminen ei ole aina helppoa. Mahdollisesta haastavuudestaan huolimatta funktionaalisen ohjelmoinnin opetteleminen tuo ohjelmoijalle uusia näkökulmia ohjelmointiongelmien ratkaisemiseen.

Lähteet 19 ASS96 Abelson, H., Sussman, G. ja Sussman, J., Structure and Interpretation of Computer Programs. The MIT Press, Cambridge, Massachusetts, toinen painos, 1996. URL http://mitpress.mit.edu/sicp/. [10.12.2012] Sivunumerot viittaavat kirjan verkkoversioon. Chu41 Church, A., The calculi of lambda-conversion. Princeton university press, Princeton, 1941. Ghc12a Data.Char, Haskellin dokumentaatio, http://www.haskell.org/ghc/ docs/latest/html/libraries/base/data-char.html. [10.12.2012]. Ghc12b Data.List, Haskellin dokumentaatio, http://www.haskell.org/ghc/ docs/latest/html/libraries/base/data-list.html. [10.12.2012]. Ghc12c GHC/Base.lhs, Haskellin standardikirjasto, http://hackage. haskell.org/packages/archive/base/latest/doc/html/src/ GHC-Base.html#map. The University of Glasgow, [2.12.2012]. HHP07 Hudak, P., Hughes, J., Peyton Jones, S. ja Wadler, P., A history of haskell: being lazy with class. Proceedings of the third ACM SIGPLAN conference on History of programming languages. ACM, 2007, sivut 12 1 12 55. Hin09 Hinsen, K., The promises of functional programming. Computing in Science & Engineering, 11,4(2009), sivut 86 90. Hud89 Hudak, P., Conception, evolution, and application of functional programming languages. ACM Computing Surveys, 21,3(1989), sivut 359 411.

20 Hug89 Hughes, J., Why functional programming matters. The Computer Journal, 32,2(1989), sivut 98 107. LTh09 Läufer, K. ja Thiruvathukal, G. K., The promises of typed, pure, and lazy functional programming: part ii. Computing in Science & Engineering, 11,5(2009), sivut 68 75. McC60 McCarthy, J., Recursive functions of symbolic expressions and their computation by machine, part i. Communications of the ACM, 3,4(1960), sivut 184 195. MMH97 Milner, R., Mads, T., Harper, R. ja MacQueen, D., The Definition of Standard ML (Revised). The MIT Press, Cambridge, Massachusetts, 1997. Sco09 Scott, M. L. Programming language pragmatics. Elsevier/Morgan Kaufmann Pub., 2009. Wad92 Wadler, P., The essence of functional programming. POPL 92 Proceedings of the 19th ACM SIGPLAN-SIGACT symposium on Principles of programming languages, 1992, sivut 1 14. Wad98 Wadler, P., Why no one uses functional languages. SIGPLAN Not., 33,8(1998), sivut 23 27.