Matemaattisen ajattelun kova ydin loistaa käytännön ohjelmointitöissä yhä kirkkaammin

Digitalisaatiopuheen jälkimainingeissa ohjelmointitaito katsottiin Suomessa niin tärkeäksi, että aihe lisättiin Suomen peruskoulujen opinto-ohjelmiin 2010-luvun lopulla. Opettajille on jo hyvän aikaa järjestetty erilaisia koulutuksia aiheesta. Varsinkin matematiikan opettajien keskuudessa on pohdittu, millainen ohjelmointityyli tukee matemaattisen ajattelun kehittymistä.

Algoritmisesta ajattelusta ohjelmointiparadigmoihin

Lukuisissa verkkokeskusteluissa ja aiheeseen liittyvissä artikkeleissa viljellään sanaparia algoritminen ajattelu. Sanaparilla kuvataan logiikkaa tai vaiheita, joiden lopputuloksena ohjelmakoodin nähdään syntyvän. Vaiheiden tai askelten korostaminen on omiaan johtamaan siihen, että oppija alkaa nähdä ongelmanratkaisun koodiriveinä tai lauseina, joita suoritetaan algoritmin vaiheiden mukaisesti, yksi kerrallaan. Hyvin usein algoritmista ajattelua verrataan ruokareseptiin – ruoka-annos syntyy reseptin mukaisesti ja reseptin mukaan toimiminen nähdään ajassa etenevinä erillisinä tapahtumina.

Reseptirinnastukseen liittyy kuitenkin ongelma, joka piilee sen ”pykälittäisyydessä”. Analogia korostaa työvaiheita ja tekemisen meininkiä – otetaan puuro uunista ja ripotellaan kanelia päälle.

Tiedon käsittelyyn liittyvien ongelmien helposti tulkittavissa olevat kuvaukset eivät kuitenkaan esitä peräkkäin suoritettavia toimintosarjoja vaan pikemminkin kuvaavat tiedolle tehtäviä muunnoksia. Kyse ei nykyaikaisissa tiedonkäsittelytehtävissä ole niinkään tietokoneelle annettavista käskyistä ja niiden kuvaamisesta vaan tiedonkäsittelysuunnitelman kuvaamisesta.

Ohjelmien kirjoittaminen on yhä enemmän yhdessä toimivien tietoa käsittelevien yksiköiden (funktio) yhteistoiminnan kuvaamista. Valtavirraksi muodostunut imperatiivinen ohjelmointityyli on käskykeskeistä ja sen avulla on vaikea ilmaista, miten tietoa muutetaan muodosta toiseen.

Tämä ei kuitenkaan tarkoita, että reseptivertauskuva olisi yleisesti erityisen huono malli algoritmisen ajattelun vertauskuvaamiseksi, vaan sitä, että reseptivertauskuva on huono ongelmanratkaisun kuvausmalli, jos sen kuvausvoima halutaan ulottaa myös sellaisiin tiedonkäsittelytilanteisiin, joihin se ei sovellu. Yläkoulussa ja varsinkin lukioiässä reseptivertauskuva voi johtaa liian suppeaan käsitykseen ohjelmoinnista.

Miksi funktionaalista ohjelmointityyliä tarvitaan?

Vielä muutama vuosi sitten verkon suosituimmat palvelut laadittiin pääsääntöisesti imperatiivisilla ohjelmointikielillä ja-menetelmillä. Tänä päivänä maailmasta löytyy yhä enemmän ja enemmän ohjelmistosuunnittelijoita, jotka luottavat funktionaalisista kielistä tuttuihin käsitteisiin, vaikka eivät puhtaan funktionaalisella kielellä kirjoittaisikaan. Esimerkiksi rahoitustoimialalla, missä kaavojen ja laskelmien esittäminen on funktionaalisen kielen avulla hyvin luontevaa, on funktionaalisia kieliä käytetty jo vuosia.

Funktionaalinen ohjelmointityyli ei kuitenkaan ole enää pelkkää laskemista. Uusimmat versiot tunnetuimmista ohjelmistokehitystyökaluista johdattelevat käyttäjänsä nimittäin laatimaan esimerkiksi verkkoselaimessa pyörivät vuorovaikutteiset käyttöliittymät pelkkiä funktioita käyttäen. Kyse on siis ohjelmistoista, joista jokainen meistä käyttää lähes päivittäin verkon palveluja käyttäessään.

Tämä tarkoittaa, että imperatiivisesta ohjelmointityylistä monille tutulta muuttujan käsitteeltä on pudonnut pohja pois, sillä muuttujia ei enää monissa ohjelmointitöissä tarvita. Viimeisin esimerkki käytännön ohjelmointityön siirtymisestä kohti funktionaalista ohjelmointityyliä on tuhansien ohjelmistosuunnittelijoiden käyttämä Facebookin kehittämä React -kirjasto, joka tarjoaa vuosi vuodelta yhä funktionaalisempia ratkaisumalleja arkipäivän ohjelmointiongelmiin. Sivuvaikutuksena tästä kehityksestä moni kokenut ohjelmistosuunnittelija pyrkii nyt eroon muuttujista sekä ohjelmakoodissaan että tavassaan ajatella ohjelmoinnista. Miksi ohjelmointityyli ja valtavirran käyttämät ohjelmointivälineet sitten muuttuvat?

Yksi merkittävä syy paradigman siirtymiseen kohti funktionaalista ohjelmointityyliä on tarve käsitellä yhä suurempia määriä tietoa. Tietoa muutetaan muodosta toiseen, etsitään tiedosta säännönmukaisuuksia tai koostetaan tiedosta erilaisia yhteenvetoja. Tämä johtaa tarpeeseen luoda tiedonkäsittely-yksikköjä, jotka on helppo sovittaa toisiinsa ja näin muodostaa näistä tiedonkäsittely-yksikköjä. Käytännössä nämä tiedonkäsittely-yksiköt ovat funktioita ja niiden yhdistelmiä mahdollisimman luettavasti esitettyinä.

Toinen merkittävä syy funktionaalisen ohjelmointityylin suosioon on, että sen avulla voidaan kirjoittaa virheettömämpiä ohjelmia. Eri puolilla ohjelmistoa esiintyvät muuttujat aiheuttavat usein päänvaivaa esiintyivätpä ne missä tietojärjestelmän osassa tahansa. Ja koska tänä päivänä laaditaan erityisen paljon juuri erilaisia verkkopalveluja, myös virhetilanteet ja ongelmat kasaantuvat tämäntyyppisiin järjestelmiin. Yhtenä ratkaisuna on muuttujallisten ja muuttujattomien ohjelman osien eristäminen toisistaan. Muuttujaton puoli ohjelmistosta laaditaan funktionaalisella ohjelmointikielellä tai ohjelmointikielellä, jossa on hyvä tuki muuttujattoman ohjelmakoodin laatimiseen.

Kolmas merkittävä syy funktionaalisen ohjelmoinnin suosioon piilee sen ilmaisuvoimaisuudessa. Funktionaalisella tyylillä kirjoitetusta ohjelmasta on imperatiivista huomattavasti helpompi sanoa, mitä se saa aikaan.

Neljäs, muttei vähäisin syy funktionaalisen ohjelmointityylin suosioon on sen hyvä soveltuvuus rinnakkaiseen tietojenkäsittelyyn. Koska tietoa pitää käsitellä suuria määriä, niin mahdollisuus käsitellä tietoa rinnakkain on tärkeää. Funktionaalinen ohjelmointityyli muuttujattomana tarjoaa hyvän ratkaisun tiedon rinnakkaiseen käsittelyyn.

Viides syy funktionaalisen ohjelmointityylin suosioon on, että koska siinä ei ole muuttujia, oppilaalla on yksi käsite vähemmän opiskeltavana. Lisäksi funktionaalisesta ohjelmoinnista on suora yhteys SQL-kieleen, joka on maailman eniten käytetyin kieli rakenteisen tiedon käsittelyssä. SQL-kielessä ei siinäkään ole muuttujia, vaan se perustuu tiedon käsittelyn kuvaamiseen tietyn syntaksin ja semantiikan avulla. Monelle SQL-kielen ymmärtäminen ja soveltaminen voi olla paljon hyödyllisempää kuin se, miten jollain imperatiivisella ohjelmointikielellä tehdään pieni peliohjelma.

Mitä matemaattista siinä sitten on?

Mutta miten tämä kaikki liittyy matematiikkaan? Ensimmäinen yhtäläisyys on, että yhtäsuuruusmerkki funktionaalisessa kielessä, kuten Haskell, merkitsee samaa kuin yhtäsuuruusmerkki matematiikassa. Näinhän ei ole lausekielissä, joissa yhtäsuuruusmerkki tarkoittaa (yleensä) sijoitusoperaatiota.

Haskell -kielen merkintä f(x) = x*2 tarkoittaa, että f on nimi, joka tarkoittaa arvoa, joka saadaan, kun lausekkeen x*2 x:n paikalle sijoitetaan jokin arvo ja lausekkeen arvo selvitetään. F tarkoittaa siis samaa kuin x*2, ei sijoitusta.

Funktionaalisissa ohjelmointikielissä ei ole imperatiivisista ohjelmointikielistä tuttuja lauseita. Koska lauseita, muuttujia ja sijoitusoperaatiota ei ole, ohjelman suoritus perustuu lausekkeiden arvon selvittämiseen eli evaluointiin. Funktionaalisen ohjelman voi kuvitella sieventämiseksi – sulkeita poistamalla ja arvoja laskemalla edetään kohti lopputulosta. Sieventäminen on kaikille matematiikan perusteet hallitsevalle tuttu käsite.

Yksi funktionaalisen ohjelmointikielen vahvuuksista on, että kirjoitettavasta ohjelmasta tulee luonnostaan erittäin yleiskäyttöistä. Vaikka kompositio (funktioiden yhdistely) onkin funktionaalisen ohjelmointityyliin juju, funktioita ei kuitenkaan kannata yhdistellä niin, että ohjelman luettavuus kärsii. Ohjelmoijan huoleksi jää siis yhtäältä komposition järkevä käyttö ja toisaalta funktioista saatavien arvojen sovittaminen toisten funktioiden parametreiksi.

Toinen funktionaalisen ohjelmointityylin vahvuuksista on, että se sopii erittäin hyvin tietointensiivisten eli paljon tietoa käyttävien sovellusalueiden sovellusten laatimiseen. Koska funktioita on helppo yhdistellä ja niiden suorittaminen rinnakkain on helppoa, soveltuu ohjelmointityyli erittäin hyvin tekoäly ja data-analyysisovelluksiin, jotka ovat tyypillisiä tietointensiivisiä sovellusalueita.

Kolmas Haskellin kaltaisen vahvasti tyypitetyn funktionaalisen ohjelmointikielen vahvuus on, että jos ohjelmakoodin saa kirjoitettua ja käännettyä, se myös toimii. Kääntämisellä tarkoitetaan ohjelmakoodin muuttamista tietokoneen ymmärtämään muotoon ennen ohjelman suorittamista. Tietyn toimialan ilmiöitä kuvaava tyyppijärjestelmä on helppo laatia ja se takaa, että ohjelmoija voi kirjoittaa vain tyyppijärjestelmän kannalta mielekästä ohjelmakoodia. Funktionaalisen ohjelmointikielen avulla onkin helppo kirjoittaa ongelmakohtainen kieli eli ikään kuin ohjelmointikieli ohjelmointikielen sisään kuvaamaan jonkin ongelma-alueen käsitteistöä.

Tyyppijärjestelmän kuvaaminen ja omien tyyppien laatiminen perustuu usein matematiikasta tuttuihin kysymyksiin. Voidaanko tyypistä luotavia alkioita vertailla ja jos voidaan, voidaanko sanoa jonkin alkion olevan järjestyksessä ennen jotain toista samantyyppistä alkiota? Milloin voimme sanoa, että kaksi tietyntyyppistä alkiota ovat samoja? Mikä on tietyntyyppisten alkioiden joukossa alkiota n seuraava alkio?

Neljäs Haskellin vahvuuksista on sen yksinkertainen käsitejärjestelmä. Henkilö, joka ei ole koskaan ohjelmoinut, voi suhteellisen pienellä vaivalla oppia laatimaan Haskell -kielisiä ohjelmia. Tästä on paljon hyviä kokemuksia ohjelmointia ensimmäistä kertaa kokeilevien ja myönteisesti matematiikkaan suhtautuvien aikuisten kanssa. Haskellin oppimiselle ei ole esteitä varsinkaan, jos funktion käsite on tuttu.

Selma-koira tietää, että hyvällä munkilla ja ahkeralla harjoittelulla funktionaalisesta ohjelmoinnista saa hyvän vainun.

Viides Haskell -kielen vahvuus on sen erinomainen kyky päätellä tyyppejä, vaikka niitä ei olisi erikseen määriteltykään. Tämä auttaa erityisesti ohjelmoinnin alkeita opiskellessa, sillä tyyppien käsitteeseen liittyvän teorian voi opettaessa sivuuttaa ja siitä huolimatta oppilaan on mahdollista päästä hyvin jyvälle sekä funktioista että ehtolausekkeista eli siitä missä ohjelmoinnissa ja erityisesti algoritmisessa ajattelussa on pohjimmiltaan kyse.

Tyyppipäättely on erinomainen ominaisuus ensimmäisessä opeteltavassa ohjelmointikielessä, sillä ohjelmoinnin logiikan oivaltaminen ei edellytä tyypin käsitettä. Listan käsitteellä pääsee pitkälle ja lista tuntuu olevan monelle oppilaalle tuttu käsite. Vaikka listan käsite Haskellissa edustaakin funktoria, monadia, applikatiivia ja alternatiivia, ei aivan kaikkea tarvitse selvittää alkeita opetellessa.

Miten funktionaalista ohjelmointia voi oppia?

Funktionaalisen ohjelmoinnin oppimisen kannalta olisi oleellista laajentaa funktion käsite koskemaan myös sellaisia arvo- ja tulosjoukkoja, jotka eivät ole esimerkiksi luonnollisia lukuja tai mitään muutakaan perinteisessä mielessä ”laskettavaa”. Ajatus johtaa oppijan helposti kategoriateorian pariin, mutta funktionaalisen ohjelmointikielen voi vallan mainiosti oppia ilman kategoriateoriaakin. Tosin kategoriateoriaan tutustuja saa oppimiseensa syvyyttä, jollaisesta kategoriateoriaan perehtymätön ei osaa edes haaveilla.

Vaikka funktion käsitteellä pääseekin pitkällä, edellyttää funktionaalisen ohjelmoinnin parempi hallinta myös funktorin käsitteeseen tutustumista. Kun funktorin kanssa pääsee sinuiksi, moni muukin asia aukeaa, kuten applikatiivin ja monadin käsitteet.

Kun funktionaalisen ohjelmoinnin ideat alkavat avautua, funktorista tulee hyvin äkkiä käsitteistön perustyökalu, jonka päälle on hyvä pinota muita käsitteitä. Ei liene yllätys, että funktorin idean oppii parhaiten harjoittelemalla ja sopivia harjoitustehtäviä ratkomalla.

Algebraan perehtyneelle matematiikkaa opettavalle tai opiskelijalle Haskell on helppo omaksuttava, sillä käsitteistössä on paljon samaa. Monoidi tarkoittaa Haskell -kielessä samaa kuin matematiikassa. Sama pätee puoliryhmiin ja muihinkin abstraktin algebran peruskäsitteisiin. Liitäntä- ja vaihdantalakien ymmärtämisestä ohjelmoinnin yhteydessä on paljon hyötyä, sillä niiden avulla on mahdollista arvioida esimerkiksi missä määrin ohjelmakoodia voidaan suorittaa rinnakkain. Alkeita opiskeleva voi kuitenkin skipata niin monoidit kuin abstraktin algebrankin – perusasiat voi oppia pitkälti myös tekemällä.

Monen opettajan ja oppilaankin haasteena funktionaalisen ohjelmoinnin opettelemisessa on motivaation löytäminen. Monesti mietitään, että esimerkiksi Haskell -kieli on liian akateeminen ja ettei sen osaamisesta ole hyötyä ns. oikeassa elämässä, mitä se oikea elämä sitten onkaan.

Mitä oikeaan elämään ja hyötyihin tulee, niin on hyvä huomata, että funktionaalinen lähestymistapa on konkretisoitunut maailman eniten käytetyimpien verkkopalvelujen käyttöliittymissä, kuten Facebookin ja monen muun palvelun käyttöliittymissä. Nyt voi olla hyvä herätellä itseään ja kysyä, mikä siinä funktionaalisessa lähestymistavassa on niin mielenkiintoista, että sen ideoiden pohjalta rakennetaan maailman eniten käytetyimmät käyttöliittymät? Funktionaalista ohjelmointityyliä sovelletaan näissä käyttöliittymissä ihan samalla tavalla kuin matematiikkaa sovelletaan psykologiasta maantietoon ja fysiikasta tilastotieteeseen.

Toinen haaste lienee, että jos opettaja on jo omaksunut imperatiivisen ohjelmoinnin eetoksen, on hänen vaikea lähteä opettelemaan toista tapaa ajatella. Eetokseen kuuluu usein ajatus, että ”ohjelmointi on mielekästä vain, jos siitä saadaan aikaiseksi jotain näkyvää”. Jos matematiikasta ajateltaisiin samoin, matematiikkaa tuskin olisi olemassa. Mutta matematiikka kuitenkin on olemassa ja sitä sovelletaan lukemattomilla tieteenaloilla, tietojenkäsittelyssäkin.

Hiihtääkö Suomi perässä?

Kun ohjelmointikielten viimeaikaista kehitystä tutkii hieman pintaa syvemmältä niin huomaa, että lähes kaikkiin eniten käytetyimpiin ohjelmointikieliin tulee jatkuvasti funktionaalisen ohjelmointityylin piirteitä. Ruotsissa ilmiö on katsottu niin tärkeäksi, että Chalmersin yliopisto on järjestänyt hyvin organisoituja ja laadukkaita funktionaalisen ohjelmoinnin kursseja perustutkinto-opiskelijoille jo useita vuosia. Suomessa funktionaalisen ohjelmoinnin yliopistotasoinen opetus osana perusopintoja on alkutekijöissään ja useimmiten esimerkiksi Haskell -ohjelmointi on monessa opinto-ohjelmassa osana syventäviä opintoja. Ammattikorkeakoulujen opinto-ohjelmissa funktionaalinen ohjelmointi saatetaan mainita sivulauseessa, jos siinäkään.

Nykytiedon valossa funktionaalinen ohjelmointi kuuluisi luontevasti tietojenkäsittelyn perusopintoihin. Suomi seurannee Ruotsia, kunhan pääsee digiähkystään tabletteineen ja muine lieveilmiöineen pohtimaan asioita, joilla oikeasti nostetaan suomalaista osaamista ja kilpailukykyä. Koulutusleikkausten jäljiltä ja valtion velkakuormaa ajatellen ei liene kovinkaan paljon toivoa siitä, että Suomessa panostettaisiin ohjelmointikielten tutkimukseen ja kehitykseen yhtään enempää kuin ennenkään. Tutkijan paikkoja ja rahoitusta on kuitenkin kohtuullisesti tarjolla Suomen ulkopuolellakin, joten akateemista uraa pohtivan kannattaa tähyillä myös rajojen ulkopuolelle.

Tietojenkäsittelyosaamisen, matemaattisen osaamisen ja liiketoimintaosaamisen leikkauskohdasta syntyy kuitenkin kilpailukykyä, josta pieni ja metsäinen maa voisi ammentaa tekoälysovellusten ja muiden tietointensiivisten sovellusten kilpailluilla markkinoilla. Seurattuani ohjelmoinnin opetukseen liittyvää keskustelua olen hiukan kärjistäen tullut siihen johtopäätökseen, että Suomi on maailman paras maa – koulutusleikkauksillaan luovuttamaan kilpailuedun Ruotsille ja valjastamaan kirkkaimmat aivonsa käyttämään toisten suunnittelemia valmistuotteita sen sijaan, että kehittäisi niitä itse. Chalmersin yliopiston syksyllä 2018 alkavalla ja keväälle 2019 jatkuvalla Haskell -kielen peruskurssilla on viisi assistenttia kurssin vastuuopettajan lisäksi. Suomessa ei liene nähdyn viime vuosina mitään vastaavaa eikä tultane näillä näkymin näkemäänkään – sikäli kapoisiksi ovat Suomi-neidon opinahjojen hartiat säästötalkoissa menneet.

Tiettyjen matematiikan osa-alueiden tuntemus auttaa ymmärtämään funktionaalista ohjelmointia ja johtaa lopulta kilpailukykyisempiin ohjelmistotuotteisiin- ja palveluihin tietyissä tuoteryhmissä. Funktionaalinen ohjelmointi tukee suoraan matematiikan taitojen oppimista etenkin niiden rakenteiden osalta, jotka on lainattu funktionaalisiin ohjelmointikieliin suoraan matematiikasta.

MAOL ry:n kommentit lukion opintosuunnitelman algoritmeja koskevaan osaan ovat hyvin johdonmukaisia ja perusteltuja. Newtonin menetelmä ja Monte Carlo -algoritmit ovat mielenkiintoisia numeerisina ja numeromielessä ”matemaattisia”. Algoritmien avulla voidaan kuitenkin ratkaista myös muunlaisia, ei-numeerisia ongelmia, kuten vaikkapa ongelmia, joissa pitää poimia joukosta alkioita tietyin ehdoin. Mainittu ongelma ei ole luonteeltaan numeerinen, mutta luontevin ratkaisu on silti tai juuri siitä syystä funktio. Edelliseen virkkeeseen viitaten kategoriateoriaa voidaan pitää abstraktina hölynpölynä, mutta lähempi tarkastelu voi avata silmiä näkemään funktio -käsitteen sen kategoriateoreettisessa viitekehyksessä ja soveltamaan sitä myös algoritmeja pohtiessa. Jos algoritmisen ajattelun tavoitteena on mahdollisimman laaja yleissivistys aiheen tiimoilta, niin algoritmisen ajattelun oppimistavoitteet eivät välttämättä toteudu parhaiten numeroiden kanssa vaan ilman niitä.

Matematiikan opettajat lienevät paljon vartijoina herättämään uteliaisuuden tutkia jotain sellaista, mikä ei vilku, ääntele tai kävele – mutta jonka avulla tieto muuttaa muotoaan ajasta ja paikasta riippumatta. Ala-asteella lienee mukavaa ottaa tuntumaa ohjelmoinnin käsitteisiin ”käskemällä konetta” ja ”liikuttamalla hahmoa”, mutta yläasteella ja viimeistään lukioiässä voi olla viisasta tutkia ohjelmoinnin muitakin ulottuvuuksia. Uteliaisuuden herättäminen ei maksa mitään ja ne oppilaista, jotka jatkavat tietojenkäsittelyn parissa aikuisikään ja innostuvat opiskelemaan aiheesta lisää, löytävät kyllä paikkansa jos eivät Suomesta, niin hyvistä opinahjoista Suomen ulkopuolelta – muuttujilla tai ilman.

Kirjoittaja