Funktionaalinen ohjelmointi on hirveän monille teknisesti muutoin valistuneille täysin vieras käsite. Olen huomannut tämän ihan konkreetisesti käydessäni netti- ja livekeskusteluita tuttavieni kanssa. Useimpien ensimmäinen reaktio on yrittää selittää funktionaaliset käsitteet proseduraalisen ohjelmoinnin kautta. Tämä on vähän sama, kuin yrittäisi hahmottaa miltä vieras hedelmä maistuu katselemalla vain sen värejä ja muotoja televisiosta. :-)
Koska aihe kiinnostaa minua, ajattelin käsitellä tässä muutamaa funktionaalisen ohjelmoinnin hyödyllistä ominaisuutta. Argumenttini on, että nämä ominaisuudet pystyvät tekemään koodista tehokkaampaa, selkeämpää ja bugittomampaa kuin perinteinen ohjelmointi. Esimerkkikieliksi vertailussa sopivat PHP, jota koodataan useimmiten erittäin proseduraalisesti, sekä Scala, joka puolestaan on hyvin funktionaalinen kieli.
Tiedon funktionaalinen käsittely bugittomasti
Käytin jo aiemminkin esimerkkinä artikkelien luuppausta läpi tähän PHP:lle ominaiseen tyyliin:
$results = array();
for ($i = 0; $i < count($articles); $i++) {
if ($articles[$i]->published) {
$results[] = $articles[$i]->title;
}
}
Ylläolevassa koodissa on seitsemän toisistaan täysin irrallista asiaa, jotka voivat mennä pieleen, jos ohjelmoija typottaa tai tekee ajatusvirheen. Ne ovat:
- Tuloslistan alustus ($results = array())
- For-loopin alustus ($i = 0)
- For-loopin ehdon tarkistus ($i < count($articles))
- For-loopin laskurin inkrementointi ($i++)
- Tietyn artikkelin ottaminen taulukosta ($articles[$i]) - kahteen eri kertaan
- Artikkelin lisääminen tuloslistan perään ($results[] = ...)
Ratkaisevaa tässä on se, että ohjelmointikieli ei pysty mitenkään yhdistämään näitä asioita toisiinsa ja havaitsemaan mahdollisia virheitä automaattisesti. For-loopissa saatetaan inkrementoida vahingossa väärää muuttujaa, toinen $articles[$i] saatetaan ottaa vahingossa väärällä indeksillä, tuloksia saatetaan lisätä vahingossa eri listaan kuin joka alustettiin, ja niin edelleen. Bugit syntyvät siitä, että nämä asiat jätetään ohjelmoijan henkilökohtaisen tarkkaavaisuuden vastuulle.
Funktionaalinen tapa tehdä sama asia (tällä kertaa Scala-kielellä) voisi mennä näin:
for (article <- articles if article.published) yield article.title
Huomaa miten tässä koodissa on vain yksi tunniste, article, joka sitoo sisäisen logiikan yhteen. Jos ohjelmoija osaa kirjoittaa sen oikein, mikään muu ei voi mennä pieleen. Bugeja ei siis enää synny typoista ja aivojen pienistä hairahduksista. Vaaditaan kertaluokkaa isompi ajatusvirhe, että koko transformaatio olisi jollain lailla täysin pielessä.
Tässä onkin funktionaalisen ohjelmoinnin ydin: Ohjelmakoodi ei kerro miten asia tehdään. Se kertoo mitä halutaan saavuttaa ja kääntäjä huolehtii lopusta.
Ohjelmointikielen soveltuvuus määräytyy sitten pitkälti sen mukaan, miten hyvin sen tarjoamat käsitteet ja operaatiot vastaavat niitä tavoitteita, joita ohjelmoija haluaa ohjelmallaan saavuttaa. Listojen käsittely on varsinkin web-ohjelmoinnissa erittäin tyypillinen use case.
PS. Jatkan ehkä vielä myöhemmin immutable-datatyypeistä, sivuvaikutuksettomasta ohjelmoinnista ja pattern matchingista, jotka ovat muita funktionaalisen ohjelmoinnin hyödyllisiä ominaisuuksia.