Lectio praecursoria

Arvoisa kustos,
kunnioitettu vastaväittäjä,
hyvät kuulijat:

lectio-slides-0

Jokaisella meistä on salasanoja, jotka suojaavat meidän omia tietojamme ja varmistavat, että kukaan ei pysty esiintymään meinä ilman lupaamme. Salasana voi kuitenkin turvata tietojamme ja identiteettiämme vain, jos ohjelma toimii oikein sen tarkastaessaan.

lectio-slides-1

Sattuipa muutama vuosi sitten niin, että eräs tietokoneohjelma tarkisti salasanan oikeellisuuden käyttämällä apurutiinia memcmp, joka raportoi vertailun tuloksen kokonaisluvulla: jos salasana oli oikea, tulos oli nolla; ja jos tulos ei ole nolla, salasana oli väärä. Koska tulos piti katkaista mahtumaan tietoalkioon, jonka tyyppi on my_bool, tulos jaettiin ilman eri käskyä luvulla 256 ja jakojäännöstä käytettiin tekemään päätös: jos jakojäännös oli nolla, salasanan katsottiin olleen oikein. Mutta miten käy, jos apurutiini palauttaakin luvun 512? Jakojäännös on nolla, vaikka salasana oli väärin. Tämän virheen ansiosta oli kenen tahansa käyttäjätunnusta mahdollista käyttää pienellä vaivalla tietämättä salasanaa. Vika on totta kai kauan sitten korjattu.


Kun menemme verkkopankkiin, luotamme, että kukaan asiaton ei pääse väliin nuuhkimaan, mitä teemme. Teknisesti tämä taataan niin, että pankin ja oman koneemme välinen liikenne salakirjoitetaan ja pankki lähettää koneellemme digitaalisen allekirjoituksen, jolla koneemme varmistaa, että vastapuoli todella on pankki eikä joku hyökkääjä. Tämä kuitenkin toimii vain, jos koneemme ohjelmisto tarkistaa digitaalisen allekirjoituksen asianmukaisesti.

lectio-slides-2

Eräs erittäin yleisesti käytetty ohjelmisto valmistautui verkkopankin lähettämän digitaalisen allekirjoituksen tarkistamiseen tekemällä useita valmistelutoimenpiteitä, joista mikä tahansa voi epäonnistua. Niinpä ohjelmisto aina valmistelutoimenpiteen lopuksi tarkisti, epäonnistuiko toimenpide, ja mikäli niin kävi, hyppäsi toisaalle ohjelmaan. Tämä on aivan normaalia. Jostain syystä erään tällaisen tarkistuksen jälkeen ohjelmassa oli vielä pari vuotta sitten ylimääräinen hyppykäsky. Ensimmäinen goto fail tuli suoritettavaksi, jos sitä edeltävä toimenpide epäonnistui. Toinen suoritettiin joka tapauksessa. Niinpä sen jälkeen tulevaa varsinaista tarkastusta ei koskaan tehty. Hyökkääjän oli mahdollista tekeytyä verkkopankiksi ilman, että kukaan huomaa mitään. Tämäkin vika on sittemmin korjattu.

Molempien vikojen taustalla on ohjelmointivirhe, jota pahentaa jokin käytetyn ohjelmointikielen erityispiirre.


Kaikki tietokoneohjelmat kirjoitetaan jollakin ohjelmointikielellä. Kuten luonnolliset kielet, esimerkiksi suomi tai englanti, ne koostuvat sanoista joista kootaan laajempia ilmaisuja. Toisin kuin luonnollisten kielten tapauksessa, ohjelmointikielen käyttäjä joutuu olemaan erityisen tarkka siitä, mitä hän kirjoittaa. Jos suomeksi sanon minä esiinnyt nyt teihin, se kuulostaa kummalliselta, mutta jokainen ymmärtänee, mitä halusin sanoa. Jos tietokoneelle kirjoittaa kaksi kertaa peräkkäin goto fail, tietokone ei pohdi, onko tämä järkevää, vaan tekee sen, mitä käsketään.

Kaikki ohjelmointikielet ovat keinotekoisia kieliä. Tietokone ei ymmärrä yhtäkään niistä itsestään, vaan jonkun tulee laatia tietokoneohjelma, joka tulkkaa tai kääntää kielellä kirjoitetun ohjelman tietokoneen ymmärtämään muotoon. Tätä kutsutaan kielen
toteuttamiseksi. Joko toteuttamisen yhteydessä tai sitä ennen pitää jonkun suunnitella kieli eli päättää, millaiset ilmaisut siinä ovat sallittuja ja mitä ne tarkoittavat. Myös jo olemassaolevia kieliä muokataan aika ajoin; muokkaukset suunnitellaan ja sitten (tai samaan aikaan) toteutetaan.


Ohjelmointikielten tutkimusta on tehty lähes niin kauan kuin tietokoneita on ollut olemassa, aivan 1950-luvulta lähtien. Valtaosa tutkimuksista selvittää, millaisia ohjelmointikieliä voisi olla olemassa, ja ratkoo ohjelmointikielten teknisiä ongelmia. Niiden tutkimusote on matemaattis-teoreettinen. Vasta 1970-luvulla alettiin tutkia, millaiset ohjelmointikielten suunnitteluratkaisut johtavat todellisten ihmisten käsissä parhaisiin lopputuloksiin. Tutkimusasetelma tässä on tyypillisesti empiirinen ja kokeellinen.

lectio-slides-3

Ensimmäisen tällaisen tutkimuksen julkaisivat Max Sime, Thomas Green ja D. J. Guest vuonna 1973. He antoivat koehenkilöiden laatia ruoanvalmistusohjeita ohjelmointikieltä muistuttavalla keinotekoisella kielellä. Osa koehenkilöistä käytti kielen versiota nimeltä JUMP, jossa käskyt seuraavat toisiaan ja tilannekohtainen valintapäätös johtaa hyppyyn yli osan käskyistä. Muiden koehenkilöiden käyttämässä kielen versiossa nimeltään NEST ohjelmalla oli hierarkinen rakenne. NEST-kieltä käyttäneet koehenkilöt pärjäsivät keskimäärin olennaisesti paremmin kuin JUMP-kieltä käyttäneet.

lectio-slides-4

Muutama vuosi myöhemmin he julkaisivat jatkotutkimuksen, jossa ehdon alle tuli voida laittaa useampi kuin yksi käsky. JUMP oli edelleen samanlainen kieli, mutta NEST oli jaettu kahdeksi vaihtoehdoksi. Ensimmäinen vaihtoehto, nimeltään NEST-BE, muistutti nykykieliä niin, että useiden käskyjen jonon ympärille kirjoitetaan BEGIN ja END. Toisessa vaihtoehdossa, NEST-INE, ehtorakenteen päättyminen ilmaistaan myös sanalla END, mutta ehto toistetaan kolmesti. Nyt NEST-INE hakkasi molemmat vaihtoehtoiset ratkaisut selvästi, mutta NEST-BE ja JUMP voittivat toisensa eri kategorioissa.

Aiemmin esittelin teille goto fail -virheen, jossa ohjelmoija oli vahingossa toistanut hyppykäskyn. Simen ja kumppanien jo 1970-luvulla julkaistujen tutkimusten terminologialla sanoen ohjelmoija oli käyttänyt C-kieltä JUMP-kielenä. Hän olisi voinut käyttää sitä myös NEST-BE-kielenä. Herää kysymys, olisiko C-kielen suunnittelussa pitänyt ottaa huomioon Simen ja kumppanien tutkimustulokset ja estää C-kielen käyttäminen JUMP-kielenä. Olisiko jopa pitänyt valita NEST-INE?


lectio-slides-5

Käsitykseni mukaan käytännön ohjelmointikielten suunnittelutyössä ei juurikaan oteta huomioon tämänkaltaisia tutkimustuloksia. Voi olla, että suunnittelijat eivät tunne tätä tutkimuskirjallisuutta tai eivät osaa tulkita sitä asianmukaisesti. Vastaavanlaisen ongelman ratkaisuksi lääketieteessä kehitettiin muutama vuosikymmen sitten lähestymistapa, jota vuonna 1992 alettiin kutsua nimellä evidence-based medicine}, suomeksi kaiketi näyttöön perustuva lääketiede. Siinä yksittäinen lääkäri voi, jos on epävarma potilaan asianmukaisesta hoidosta, selvittää asiaa tutkimuskirjallisuuden avulla. Ensiksi hän muotoilee kysymyksen, toiseksi hän etsii tutkimuskirjallisuudesta kysymykseen mahdollisesti vastaavia tutkimusraportteja, kolmanneksi hän arvioi löytyneiden tutkimusten luotettavuuden ja hyödyllisyyden, neljänneksi soveltaa löytynyttä vastausta käytäntöön ja viidenneksi arvioi omaa suoriutumistaan.

Nyt tarkastettavan väitöskirjani lähtökohtana on hypoteesi, että vastaavanlainen lähestymistapa saattaisi olla hyödyllinen myös ohjelmointikielten suunnittelussa. Välittömästi esiin nousee useita kysymyksiä:

lectio-slides-6

Ensiksi, mitä tämä evidence eli näyttö on, josta näyttöön perustuvassa toiminnassa on kyse?

Toiseksi, kuinka paljon tähän käyttöön soveltuvaa tutkimusta on julkaistu?

Kolmanneksi, millaiseksi näyttöön perustuva lääketiede pitäisi sovittaa, jotta se soveltuisi ohjelmointikielten kehitykseen?

Näihin kolmeen kysymykseen on vastattava, ennen kuin voidaan edes ryhtyä selvittämään, olisiko näyttöön perustuvasta ohjelmointikielten kehityksestä mitään todellista hyötyä. Näihin kysymyksiin vastaan väitöskirjassani.


Kysymys evidenssin eli näytön luonteesta osoittautui huomattavan hankalaksi. Lähdin alun perin siitä oletuksesta, että asia on tutkittu ja ratkaistu kirjallisuudessa, jota en tunne. Osallistuin keväällä 2011 näyttöön perustuvan ohjelmistotekniikan tutkimuskonferenssiin Evaluation and Assessment in Software Engineering Englannin Durhamissa. Sopivalla hetkellä kysyin eräältä senioritutkijalta vinkkejä. Tuli nopeasti selväksi, ettei hän otttanut kysymystä vakavasti. Kaivelin myös ohjelmistotekniikan alan kirjallisuutta, ja sieltä tuli vastaan sama hyvin pinnallinen näkemys: näyttö on niin itsestään selvä käsite ettei sitä tarvitse analysoida.

lectio-slides-7

Etsin seuraavaksi vastausta lääketieteen kirjallisuudesta. Pohjimmiltaan näyttöön perustuvassa lääketieteessä näytöllä tarkoitetaan julkaistua tutkimustulosta, ja sen luotettavuuden ajatellaan riippuvan lähes pelkästään käytetystä tutkimusasetelmasta: kontrolloitu koe on parempi kuin mikä tahansa muu, mukaan lukien teoreettinen pohdinta, ja satunnaistettu koe, eli koe, jossa koehenkilöt jaetaan ryhmiin arpomalla, on parempi kuin sellainen, jossa näin ei tehdä. Järjestelmälliset kirjallisuuskatsaukset ovat puolestaan parempia kuin alkuperäistutkimukset, ja niistä parhaimmistoa ovat ne, jotka jättävät kaiken muun kuin satunnaistetut kokeet huomiotta. Tällaista näyttöhierarkiaa käytetään ohjaamaan sekä kirjallisuushakuja että löydettyjen tutkimusten laadun arviointia: jos löytyy yksikin satunnaistettuja kokeita tarkasteleva järjestelmällinen katsaus, muita tutkimuksia ei edes etsitä.

Näyttöhierarkia vaikuttaa päällisin puolin aivan mainiolta idealta. Se on yksinkertainen sääntö, jolla yksittäisen lääkärin kirjallisuushaut saadaan pidettyä pieninä, ja se vähentää tarvetta oikeasti pohtia löydettyjen tutkimusraporttien sisäisen logiikan pitävyyttä. Mutta mihin perustuu väite, että näin toimimalla löydetään oikeasti lähimpänä totuutta olevat tutkimukset? Lääketieteen tutkimuskirjallisuudessa näyttöhierarkiaa onkin kritisoitu.

Pohjimmiltaan siinä, pitääkö joku näyttöhierarkiaa hyvänä vai huonona ideana, on kyse tieteenfilosofisista näkemyseroista.


lectio-slides-8

Akateemisessa tieteenfilosofiassa keskeinen kysymys 1700-luvulta alkaen on ollut induktion ongelma. Miksi siitä, että aurinko on noussut tähän mennessä joka päivä, voi päätellä, että se nousee huomennakin? Vai onko tällainen päätelmä ylipäätään sallittu? — Onneksi väitöstilaisuuteni pidetään Jyväskylässä, jossa tämä esimerkki vielä toimii; Utsjoellahan aurinko ei nouse huomenna!

Loogiset positivistit, joita sittemmin kutsuttiin loogisiksi empiristeiksi, pyrkivät luomaan induktiolle logiikan; tähän projektiin osallistuivat muiden muassa suomalaiset Jaakko Hintikka ja Georg Henrik von Wright sekä saksalaissyntyiset Peter Hempel ja Rudolf Carnap.

Samaan aikaan todennäköisyyslaskennan teoria kehittyi valtavasti, ja tilastotieteen teoria jakaantui useaan koulukuntaan. Jakolinjana oli suhtautuminen käänteisen todennäköisyyden periaatteeseen, jonka idea oli johtaa hypoteesin todennäköisyys suoraan tilastoista. Valtavirraksi kehittyi Ronald Fisherin, Jerzy Neymanin ja Egon Pearsonin ajatusten sekoitus, jossa käänteisen todennäköisyyden periaate ja siten hypoteesin todennäköisyyden käsite kiellettiin. Oppositioon jäivät muiden muassa Harold Jeffreys ja Leonard Savage, joiden teorian ytimen muodosti juurikin käänteinen todennäköisyys ja jota nykyään kutsutaan Bayesiläiseksi tilastotieteeksi.

Monet tieteenfilosofit kannattavat nykyään induktion teoriaa, jossa yhdistetään loogisten empiristien tavoite Bayesiläisen tilastotieteen perusideoihin. Väitöskirjassani otan tämän lähestymistavan pohjaksi. Induktiivisen argumentin vahvuus on kunkin kuuntelijan henkilökohtainen arvio siitä, kuinka hyvin se vakuuttaa hänet. Jotta tätä arviota voidaan pitää rationaalisena, sen tulee täyttää tietyt ristiriidattomuusvaatimukset. Osoittautuu, että nämä vaatimukset johtavat kohtuullisen selvästi siihen, että argumentin vahvuus on sen loppupäätelmän subjektiivinen todennäköisyys.

Tällaisen induktion teorian pohjalta näytölle tulee luonnollinen tulkinta: näyttöä on mikä vain, joka parantaa induktiivisen argumentin vahvuutta. Millainen tutkimus vain kelpaa näytöksi, mutta riippuu kovasti tutkimuksen yksityiskohdista, parantaako se argumenttia paljon vai vähän.

Kun arvioinnin pohjaksi otetaan tämä induktion teoria, näyttöhierarkian asema jää epävarmaksi. Kontrolloitu koe on usein paras vaihtoehto, ja satunnaistaminen voi parantaa kokeen luotettavuutta. Ongelmallisempaa on perustella, miksi osa saatavilla olevasta näytöstä pitäisi rajata pois. Jokainen voi itse toki tehdä tällaisen rajauksen omasta puolestaan.


lectio-slides-9

Toinen kysymykseni oli, kuinka paljon ohjelmointikielten suunnittelun ohjaukseen soveltuvaa tutkimusta on julkaistu. Tätä varten toteutin järjestelmällisen kirjallisuuskartoituksen. Aloitin sen vuonna 2010 ja sain sen valmiiksi viime vuonna; raportoin sen alun perin lisensiaattitutkimuksenani.

Kirjallisuuskartoituksella eli mapping studylla tarkoitan kirjallisuuteen perustuvaa tutkimusta, jossa pyritään kartoittamaan jonkin tutkimusalueen yleinen tila. Tässä tapauksessa halusin selvittää, mitä asioita on tutkittu ja miten.

Se, että kartoitukseni oli järjestelmällinen, tarkoittaa, että olin sen etukäteen yksityiskohtaisesti suunnitellut ja suunnitelman mukaisesti toteuttanut. Kirjasin ylös kaiken minkä tein. Aikaahan tähän meni useita vuosia.

lectio-slides-10

Kartoituksen tuloksista nostan esille pari havaintoa. Ensinnäkin empiirisiä tutkimuksia, joissa on vertailtu eri tapoja ratkaista jokin suunnitteluongelma, on julkaistu vuodesta 1973 asti. Monta kymmentä vuotta tutkimuksia julkaistiin varsin vähän, kunnes kymmenkunta vuotta sitten jotain tapahtui, ja julkaisujen määrä lähti huomattavaan kasvuun. Kartoitukseni aineisto päättyy vuoteen 2012, mutta näppituntumani mukaan julkaisujen määrä ei ole sen jälkeen ainakaan lähtenyt laskuun.

Vielä karummalta tilanne näyttää, jos rajoitutaan tarkastelemaan pelkästään satunnaistettuja kokeita. Vuodesta 1976 vuoteen 2012 julkaistiin keskimäärin yksi satunnaistettu koe joka toinen vuosi, eikä viimeisten aineistooni kuulvien vuosien nousukaan kovin huima ollut. Vertailun vuoksi: lääketieteessä satunnaistettuja kokeita julkaistaan tuhansia joka vuosi, ehkä jopa kymmeniä tuhansia joka vuosi.

lectio-slides-11

Eniten on tutkittu sitä, miten ehtolauseet tulisi suunnitella. Aiemmat esimerkkini kuuluivat tähän kategoriaan. Myös tyyppijärjestelmiä, jotka sulkevat pois eräitä helposti koneellisesti tunnistettavia virhelajeja, on tutkittu jonkin verran; jos C-kielessä olisi ollut parempi tyyppijärjestelmä, turhaa 256:lla jakamista ei olisi todennäköisesti tapahtunut ja salasanatarkistus olisi toiminut. Kaiken kaikkiaan vaikuttaa, että varsin vähäiseen määrään suunnitteluongelmia olisi kirjallisuudesta edes periaatteessa mahdollista löytää nykyisellään vastaus.


lectio-slides-12

Kolmannen kysymykseni — eli millaiseksi näyttöön perustuva lääketiede pitäisi sovittaa, jotta se soveltuisi ohjelmointikielten kehitykseen — vastauksen lähtökohtana on sama viiden askeleen menetelmä, jota näyttöön perustuvassa lääketieteessä sovelletaan. Olen väitöskirjassani hahmotellut tarkempia ohjeita kullekin viidelle askeleelle, joita näyttöön perustuvassa ohjelmointikielen kehityksessä tulisi noudattaa. En niitä tässä käy yksityiskohtaisesti läpi, sen sijaan otan esiin muutamia yleisiä huomioita.

Näyttöön perustuvan lääketieteen pääasiallinen toimija on yksittäinen lääkäri, jolla on todellinen ongelma todellisen potilaan hoidon kanssa, ja lopulliseen hoitopäätökseen vaikuttaa kirjallisuudesta löytyneen vastauksen lisäksi potilaan tilanne ja arvot. Vastaavasti näyttöön perustuvan ohjelmointikielten kehityksen pääasiallinen toimija on yksittäinen ohjelmointikielen suunnittelija tai suunnittelijaryhmä, jolla on todellinen ongelma suunnittelutyössään ja jonka työskentelyyn vaikuttavat työn tavoitteet ja tekijöiden henkilökohtaiset tai organisaation arvot. Niinpä olennainen osa väitöskirjassa esittämäänu menetelmää on suunnittelijan vapaa harkinta siitä, mikä on olennaista. Kaksi eri suunnittelijaa voivat päätyä samalla menetelmällä samasta suunnitteluongelmasta eri tulokseen, koska heillä voi olla erilaiset painotukset ja tavoitteet sekä erilaiset toimintaa ohjaavat arvot.

Näyttöön perustuva ohjelmointikielten suunnittelu ei ole kaikenkattava menetelmä, jota tulisi soveltaa aina ja yksinomaan. Jos suunnittelijalla on selvät sävelet siitä, mitä hän haluaa tehdä, ei sitä tarvitse erikseen hyväksyttää tutkimuskirjallisuudella. Samoin jos suunnittelija on itse tutkinut jotain asiaa, ei tutkimuksen julkaisua ole tarpeen odottaa vaan sen tuloksia voi totta kai soveltaa heti oman kielen suunnittelussa.


lectio-slides-13

Väitöskirjani pääotsikko on suomeksi Näyttöön perustuva ohjelmointikielten suunnittelu, mutta kirjani ei suinkaan edes yritä olla viimeinen sana aiheestaan. Alaotsikossa esiintyy sana exploration eli tutkimusmatka kuvaamassa väitöskirjan luonnetta — kirjassa matkataan pääotsikon ympärillä selvittämässä maastoa ja piirtämässä karttaa sen lähialueista. Tutkimusmatka on filosofinen, koska yksi tutkimusmatkan kohteista on pääotsikon alla oleva maa, tieteenfilosofia ja tietoteoria; se on filosofinen myös siksi, että yksi käyttämistäni tutkimusotteista on filosofinen analyysi. Tutkimusmatka on metodologinen eli menetelmäopillinen, koska kirjassa tarkastellaan myös tiettyjä sen tekemisessä tarvittuja menetelmiä tarkemmin ja osittain myös itsenäisinä tutkimusmatkan kohteina.

May I ask you, Mr. Professor, as the Opponent appointed by the faculty, to present the comments to my dissertation that you see justifiable?

Tämän jälkeen kehotan niitä arvoisia läsnäolijoita, joilla on jotakin muistuttamista väitöskirjani johdosta, pyytämään puheenvuoron kustokselta.

Väitöstilaisuus 4.12.2015 kesti 2 tuntia ja siihen osallistuneista yksi esitti tämän kehoituksen jälkeen kysymyksen. Hänestä tuli siten ns. ylimääräinen vastaväittäjä.

Väitöstilaisuus lähestyy, väitöskirja julkaistu

Puolustan väitöskirjaani “Evidence-Based Programming Language Design: A Philosophical and Methodological Exploration” perjantaina 4. joulukuuta 2015 kello 12 Jyväskylän yliopiston vanhassa juhlasalissa (Seminarium-rakennus, sali S212). Vastaväittäjänä on professori Lutz Prechelt (Freie Universität Berlin, Saksa) ja kustoksena professori Tommi Kärkkäinen (Jyväskylän yliopisto).

Väitöstilaisuus on julkinen ja sinne on vapaa pääsy. Yleisö voi pukeutua kuten mihin tahansa arkiseen aktiviteettiin yliopistossa (esimerkiksi luennolle), juhlapukeutuminen ei ole tarpeen. Facebook-tapahtuma on.

Väitöskirjan esitarkastajina toimivat professori Matthias Felleisen (Northeastern University, Yhdysvallat) ja professori Andreas Stefik (University of Nevada, Las Vegas, Yhdysvallat). Väitöskirja sisältää merkittävän osan lisensiaattityöstäni, jonka tarkastivat viime vuonna tohtori Stefan Hanenberg (Duisburg-Essenin yliopisto, Saksa) ja professori Stein Krogdahl (Oslon yliopisto, Norja).

Väitöskirja on nyt julkisesti saatavilla PDF:nä.

YHTEENVETO

Näyttöön perustuva ohjelmointikielten kehitys. Filosofinen ja menetelmäopillinen tutkimusmatka.

Väitöskirjassani pohdin, kuinka olemassa olevaa tutkimuskirjallisuutta voisi käyttää hyväksi ohjelmointikielten kehityksessä. Vastaavaan ongelmaan lääketieteessä kehitettiin 1970–1980-luvuilla lähestymistapa, jota 1990-luvun alusta on kutsuttu näyttöön perustuvaksi lääketieteeksi. Niinpä väitöskirjassani selvitän vastaavan näyttöön perustuvan ohjelmointikielten kehityksen mahdollista olemusta.

Väitöskirjan tutkimusmenetelmänä on toisaalta filosofinen käsiteanalyysi ja toisaalta systemaattinen kirjallisuuskartoitus. Käsiteanalyysillä selvensin näyttöön perustuvan lääketieteen olemusta ja sen siirtämistä soveltuvin osin ohjelmointikielten kehitykseen. Systemaattisella kirjallisuuskartoituksella, jonka aiemmin raportoin lisensiaattityönäni, selvitin, mitä empiiristä tutkimusnäyttöä on olemassa, jota voisi ohjelmointikielten näyttöön perustuvassa kehityksessä käyttää hyväksi. Lisäksi väitöskirja sisältää laajahkon tällaisten tutkimusten metodologiaa pohtivan osuuden.

Osana metodologian tarkastelua olen väitöskirjassani tarkentanut systemaattisten kirjallisuuskartoitusten ja -katsausten menetelmää kysyen, soveltuuko Cohenin kappa aineistokoodauksen luotettavuuden arviointiin. Matemaattisilla menetelmillä vastaukseksi selvisi ei. Sen asemesta suosittelen käyttämään Krippendorffin alfaa tai Fleissin kappaa.

Empiirisen tutkimusnäytön kirjallisuuskartoituksessa, jonka aineisto ulottuu vuoteen 2012 asti, havaitsin, että ohjelmointikielten suunnitteluratkaisujen hyödyllisyyttä on tutkittu jonkin verran: kaiken kaikkiaan tutkimuksia löytyi 141, ja näistä 65 tutkimusta, jotka ovat selkeimmin hyödyllisiä kielen kehittäjille, muodostavat tulosten ytimen. Eniten on tutkittu eri tapoja ilmaista suorituksen haarautumista (11 koetta ytimessä, joista 8 kontrolloituja, joista 3 satunnaistettuja; vanhin tutkimus julkaistu 1973), valintaa staattisen ja dynaamisen tyypityksen välillä (6 tutkimusta ytimessä, joista 5 kontrolloituja kokeita, joista 4 satunnaistettuja; vanhin tutkimus julkaistu 2009), sekä eri tapoja ilmaista silmukkarakenne (5 tutkimusta ytimessä, joista 4 kokeita, joista 3 kontrolloituja ja yksi satunnaistettu; vanhin tutkimus julkaistu 1978). Hyödyllisyyttä on tutkimuksissa tarkasteltu pääasiassa virhealttiuden, ohjelmien ymmärrettävyyden sekä ohjelmointityön työläyden kautta.

Tutkimusmenetelmistä suosituin ytimessä oli (määrällinen) koe, jota käytti 41 tutkimusta. Toiseksi suosituin 11 tutkimuksella oli tutkimusasetelma, jossa olemassa olevia ohjelmia muokattiin käyttämään uutta ohjelmointikielen suunnitteluratkaisua hyväkseen. Kolmanneksi suosituin 8 tutkimuksella oli ohjelmistokorpuksen analyysi. Ytimessä käytettiin lisäksi tapaustutkimusta (2), kyselyä (2) ja ohjelmaparien analysointia (1). Ytimen kokeellisissa tutkimuksissa yleisimmin koehenkilöinä käytettiin ohjelmoijia (35 koetta), jotka tavallisimmin olivat ohjelmoinnin opiskelijoita (29 koetta).

Kartoituksen tuloksista on pääteltävissä varsin masentava kuva tämän kartoituksen alueeseen kuuluvasta tutkimusaktiviteetista. Vaikuttaa siltä, että aina silloin tällöin joku tutkija tai tutkimusryhmä keksii, että tällaiset tutkimukset olisivat hieno juttu, ja tekee niitä sitten muutaman kunnes kyllästyy ja vaihtaa aihetta. Julkaistut tutkimukset eivät vaikuttaisi inspiroineen kovin paljoa jatkotutkimuksia, eikä paradigman perustavia esimerkkitutkimuksia näytä syntyneen. On myös mahdollista, että julkaisufoorumien toimittajat ja vertaisarvioijat pitävät empiiristä tutkimusta niin hyödyttömänä, ettei sellaisia tutkimuksia juuri niiden tekemisestä huolimatta julkaista. On toki mahdollista, että viimeisen viiden vuoden aikana lisääntynyt tutkimustoiminta tarkoittaa, että tilanne on muuttunut; mutta koska lukumäärät ovat edelleen pieniä, saattaa tilanne palata jokusen vuoden jälkeen takaisin matalan aktiviteetin tasolle. Valitettavasti kartoitukseni aineistosta ei ole mahdollista päätellä mitään viime vuosien tutkimustoiminnasta.

Väitöskirjan tuloksena syntynyt näyttöön perustuvan ohjelmointikielten kehityksen hahmotelma on seuraava. Jos kielen kehittäjälle on tosiasiassa epäselvää, miten jokin kehitysongelma tulisi ratkaista, hän voi alkaa soveltaa menetelmän viisiaskelista toimintaohjetta. Ensin hän täsmentää kysymyksensä, sitten hän tekee kysymyksen perusteella kirjallisuushaut, kolmanneksi hän arvioi löytyneiden lähteiden luotettavuuden, neljänneksi soveltaa kirjallisuuden antamaa ratkaisua ongelmaansa ja viidenneksi pohtii, kuinka menetelmän soveltaminen häneltä onnistui. Tämä toimintaohje on tällä yleistasolla hyvin samanlainen kuin näyttöön perustuvassa lääketieteessä; eroja tulee, kun askelille annetaan tarkemmat toteutusohjeet.

Kuten kaikilla tutkimuksilla, tällä väitöskirjalla on rajoitteita, jotka tulee tuloksia tulkittaessa ottaa huomioon. Käsiteanalyysin tärkein rajoite on, etteivät sillä tavalla syntyvät tulokset ole todistettavissa oikeiksi; paraskaan käsiteanalyysi ei välttämättä vakuuta kaikkia lukijoita. Väitöstyöni käsiteanalyysin tavoitteena on siten ollut, että eri mieltä oleva lukija kykenee vastaamaan argumentteihini järkevillä vasta-argumenteilla ja siten edistämään aihetta koskevaa tieteellistä keskustelua. Kirjallisuuskartoituksen keskeisin rajoite puolestaan on, että julkaisujen mukaan ottamisessa ja tutkimusten koodauksessa on voinut sattua virheitä, vaikka niitä on pyritty välttämään ja löytämään. On myös mahdollista, että joitakin asiaan liittyviä tutkimuksia ei ole löytynyt hauissa eikä siksi ole kartoituksessa huomioitu.

Väitöskirjan olennaisin tulos on näyttöön perustuvan ohjelmointikielten kehityksen hahmotelma, joka soveltuu jatkotutkimuksen pohjaksi. Sitä ei ole empiirisesti arvioitu, joten lopullisena vastauksena sitä ei voi pitää. Sen käytettävissä nykyisin oleva empiirinen tutkimusnäyttö on ohutta, mutta sitä on.

Doctoral defense approaching, dissertation publicly available

I will be defending my doctoral dissertation “Evidence-based programming language design: a philosophical and methodological exploration” on December 4, 2015 at noon, in the Seminarium building, auditorium S212, of the University of Jyväskylä. My opponent will be Professor Lutz Prechelt (Freie Universität Berlin, Germany), and the custos is Professor Tommi Kärkkäinen (University of Jyväskylä).

The defense is public; anyone may come. Dress code for the audience is whatever one would wear to any lecture or regular academic activity at the university (no formal dress required). There is a Facebook event page.

The dissertation manuscript was reviewed (for a permission to publish and defend) by Professor Matthias Felleisen (Northeastern University, USA) and Professor Andreas Stefik (University of Nevada, Las Vegas, USA). The dissertation incorporates most of my licentiate thesis, which was examined last year by Doctor Stefan Hanenberg (University of Duisburg-Essen, Germany) and Professor Stein Krogdahl (University of Oslo, Norway).

The dissertation is now publicly available as a PDF.

The dissertation mentions Haskell in several places, although that is not its main focus.

ABSTRACT

Kaijanaho, Antti-Juhani
Evidence-Based Programming Language Design. A Philosophical and Methodological Exploration.
Jyväskylä: University of Jyväskylä, 2015, 256 p.
(Jyväskylä Studies in Computing
ISSN 1456-5390; 222)
ISBN 978-951-39-6387-3 (nid.)
ISBN 978-951-39-6388-0 (PDF)
Finnish summary
Diss.

Background: Programming language design is not usually informed by empirical studies. In other fields similar problems have inspired an evidence-based paradigm of practice. Such a paradigm is practically inevitable in language design, as well. Aims: The content of evidence-based programming design (EB-PLD) is explored, as is the concept of evidence in general. Additionally, the extent of evidence potentially useful for EB-PLD is mapped, and the appropriateness of Cohen’s kappa for evaluating coder agreement in a secondary study is evaluated. Method: Philosophical analysis and explication are used to clarify the unclear. A systematic mapping study was conducted to map out the existing body of evidence. Results: Evidence is a report of observations that affects the strength of an argument. There is some but not much evidence. EB-PLD is a five-step process for resolving uncertainty about design problems. Cohen’s kappa is inappropriate for coder agreement evaluation in systematic secondary studies. Conclusions: Coder agreement evaluation should use Scott’s pi, Fleiss’ kappa, or Krippendorff’s alpha. EB-PLD is worthy of further research, although its usefulness was out of scope here.

Keywords: programming languages, programming language design, evidence-based paradigm, philosophical analysis, evidence, systematic mapping study, coder agreement analysis

Dear Lazyweb: Does this software exist?

I’ve been wondering if the following kind of testing management software exists (preferably free software, of course).

It would allow one to specify a number of test cases. For each, one should be able to describe preconditions, testing instructions and expected outcome. Also, file attachments should be supported in case a test case needs a particular data set.

It would publish a web site describing each test case.

A tester (who in the free software world could be anyone) would take a test case, follow the instructions given and observe whatever outcome occurs. The tester would then file a test report with this software, either a terse success report or a more verbose failure report.

The software should maintain testing statistics so that testers could easily choose test cases that have a dearth of reports.

As a bonus, it would be nice if the software could submit a failure report as a bug report .

(Note that this would be useful for handling the sort of tests that cannot be automated. There are many good ways already to run automated test suites.)

dctrl-tools translations

dctrl-tools 1.14 (targeting squeeze) has the following incomplete translations (as of right now in git):

ca: 89 translated messages, 4 fuzzy translations, 18 untranslated messages.
cs: 108 translated messages, 1 fuzzy translation, 2 untranslated messages.
de: 111 translated messages.
en_GB: 89 translated messages, 4 fuzzy translations, 18 untranslated messages.
es: 89 translated messages, 4 fuzzy translations, 18 untranslated messages.
fi: 111 translated messages.
fr: 108 translated messages, 1 fuzzy translation, 2 untranslated messages.
it: 65 translated messages, 8 fuzzy translations, 38 untranslated messages.
ja: 89 translated messages, 4 fuzzy translations, 18 untranslated messages.
pl: 49 translated messages, 2 fuzzy translations, 60 untranslated messages.
pt_BR: 89 translated messages, 4 fuzzy translations, 18 untranslated messages.
ru: 108 translated messages, 1 fuzzy translation, 2 untranslated messages.
sv: 84 translated messages, 4 fuzzy translations, 23 untranslated messages.
vi: 89 translated messages, 4 fuzzy translations, 18 untranslated messages.

I have put the relevant pot and po files up. This is not an archival URI, but I’ll keep it available long enough for this.

Submissions through the BTS are accepted, as usual.

Debian developers and others with collab-maint access may, if they wish, push their updates directly to the Git repository. Please use the maint-2.14 branch and please read the README.

I will NOT be gathering translations from Rosetta.

All contributors and committers are asked to declare whether they can affirm the Developer’s Certificate of Origin. The commit tag Signed-off-by is, by convention, interpreted as an affirmation of that document by the person identified on that tag line.

Alue status report

In May, I posted about the discussion forum software I am writing, Alue. Since then –

What works:

  • The NNTP interface is essentially complete.
  • There is a rudimentary reading and posting HTTPS interface .
  • Users are able to self-register, manage their own accounts and reset lost passwords using an email challenge system.

What is broken:

  • NNTP control messages.
  • MIME message display in HTTPS (including character set conversions)
  • Web design. Although – all HTML is template-generated, so it’s more a problem with the test installation than with the actual software.

What is missing:

  • HTTPS-based administration
  • Moderation
  • Spam control
  • Email distribution of messages
  • Posting by email
  • Packaging (including proper installation and upgrade procedures)

And it would probably use a proper security review of the code.

If you are interested, go check out the test installation. The code and the test installation templates are available through Git. If you are really brave (and are a skilled system administrator), you might try creating your own installation – if you do, let me know.

This is Alue

I have made a couple of references in my blog to the new software suite I am writing, which I am calling Alue. It is time to explain what it is all about.

Alue will be a discussion forum system providing a web-based forum interface, a NNTP (Netnews) interface and an email interface, all with equal status. What will be unusual compared to most of the competition is that all these interfaces will be coequal views to the same abstract discussion, instead of being primarily one of these things and providing the others as bolted-on gateways. (I am aware of at least one other such system, but it is proprietary and thus not useful to my needs. Besides, I get to learn all kinds of fun things while doing this.)

I have, over several years, come across many times the need for such systems and never found a good, free implementation. I am now building this software for the use of one new discussion site that is being formed (which is graciously willing to serve as my guinea pig), but I hope it will eventually be of use to many other places as well.

I now have the first increment ready for beta testing. Note that this is not even close to being what I described above; it is merely a start. It currently provides a fully functional NNTP interface to a rudimentary (unreliable and unscalable) discussion database.

The NNTP server implements most of RFC 3977 (the base NNTP spec – IHAVE, MODE-READER, NEWNEWS and HDR are missing), all of RFC 4642 (STARTTLS) and a part of RFC 4643 (AUTHINFO USER – the SASL part is missing). The article database is intended to support – with certain deliberate omissions – the upcoming Netnews standards (USEFOR and USEPRO), but currently omits most of the mandatory checks.

There is a test installation at verbosify.org (port 119), which allows anonymous reading but requires identification and authentication for posting. I am currently handing out accounts only by invitation.

Code can be browsed in a Gitweb; git clone requests should be directed to git://git.verbosify.org/git/alue.git/.

There are some tweaks to be done to the NNTP frontend, but after that I expect to be rewriting the message filing system to be at least reliable if not scalable. After that, it is time for a web interface.

Asynchronous transput and gnutls

CC0
To the extent possible under law,
Antti-Juhani Kaijanaho has waived all copyright and related or neighboring rights to
Asynchronous transput and gnutls. This work is published from Finland.

GnuTLS is a wonderful thing. It even has a thick manual – but nevertheless its documentation is severely lacking from the programmer’s point of view (and there doesn’t even seem to be independent howtos floating on the net). My hope is to remedy with this post, in small part, that problem.

I spent the weekend adding STARTTLS support to the NNTP (reading) server component of Alue. Since Alue is written in C++ and uses the Boost ASIO library as its primary concurrency framework, it seemed prudent to use ASIO’s SSL sublibrary. However, the result wasn’t stable and debugging it looked unappetizing. So, I wrote my own TLS layer on top of ASIO, based on gnutls.

Now, the gnutls API looks like it works only with synchronous transput: all TLS network operations are of the form “do this and return when done”; for example gnutls_handshake returns once the handshake is finished. So how does one adapt this to asynchronous transput? Fortunately, there are (badly documented) hooks for this purpose.

An application can tell gnutls to call application-supplied functions instead of the read(2) and write(2) system calls. Thus, when setting up a TLS session but before the handshake, I do the following:

                gnutls_transport_set_ptr(gs, this);
                gnutls_transport_set_push_function(gs, push_static);
                gnutls_transport_set_pull_function(gs, pull_static);
                gnutls_transport_set_lowat(gs, 0);

Here, gs is my private copy of the gnutls session structure, and the push_static and pull_static are static member functions in my sesssion wrapper class. The first line tells gnutls to give the current this pointer (a pointer to the current session wrapper) as the first argument to them. The last line tells gnutls not to try treating the this pointer as a Berkeley socket.

The pull_static static member function just passes control on to a non-static member, for convenience:

ssize_t session::pull_static(void * th, void *b, size_t n)
{
        return static_cast<session *>(th)->pull(b, n);
}

The basic idea of the pull function is to try to return immediately with data from a buffer, and if the buffer is empty, to fail with an error code signalling the absence of data with the possibility that data may become available later (the POSIX EAGAIN code):

class session
{
        [...]
        std::vector<unsigned char> ins;
        size_t ins_low, ins_high;
        [...]
};
ssize_t session::pull(void *b, size_t n_wanted)
{
        unsigned char *cs = static_cast<unsigned char *>(b);
        if (ins_high - ins_low > 0)
        {
                errno = EAGAIN;
                return -1;
        }
        size_t n = ins_high - ins_low < n_wanted 
                ?  ins_high - ins_low 
                :  n_wanted;
        for (size_t i = 0; i < n; i++)
        {
                cs[i] = ins[ins_low+i];
        }
        ins_low += n;
        return n;
}

Here, ins_low is an index to the ins vector specifying the first byte which has not already been passed on to gnutls, while ins_high is an index to the ins vector specifying the first byte that does not contain data read from the network. The assertions 0 <= ins_low, ins_low <= ins_high and ins_high <= ins.size() are obvious invariants in this buffering scheme.

The push case is simpler: all one needs to do is buffer the data that gnutls wants to send, for later transmission:

class session
{
        [...]
        std::vector<unsigned char> outs;
        size_t outs_low;
        [...]
};
ssize_t session::push(const void *b, size_t n)
{
        const unsigned char *cs = static_cast<const unsigned char *>(b);
        for (size_t i = 0; i < n; i++)
        {
                outs.push_back(cs[i]);
        }
        return n;
}

The low water mark outs_low (indicating the first byte that has not yet been sent to the network) is not needed in the push function. It would be possible for the push callback to signal EAGAIN, but it is not necessary in this scheme (assuming that one does not need to establish hard buffer limits).

Once gnutls receives an EAGAIN condition from the pull callback, it suspends the current operation and returns to its caller with the gnutls condition GNUTLS_E_AGAIN. The caller must arrange for more data to become available to the pull callback (in this case by scheduling an asynchronous write of the data in the outs buffer scheme and scheduling an asynchronous read to the ins buffer scheme) and then call the operation again, allowing the operation to resume.

The code so far does not actually perform any network transput. For this, I have written two auxiliary methods:

class session
{
        [...]
        bool read_active, write_active;
        [...]
};
void session::post_write()
{
        if (write_active) return;
        if (outs_low > 0 && outs_low == outs.size())
        {
                outs.clear();
                outs_low = 0;
        }
        else if (outs_low > 4096)
        {
                outs.erase(outs.begin(), outs.begin() + outs_low);
                outs_low = 0;
        }
        if (outs_low < outs.size())
        {
                stream.async_write_some
                        (boost::asio::buffer(outs.data()+outs_low,
                                             outs.size()-outs_low),
                         boost::bind(&session::sent_some,
                                     this, _1, _2));
                write_active = true;
        }
}

void session::post_read()
{
        if (read_active) return;
        if (ins_low > 0 && ins_low == ins.size())
        {
                ins.clear();
                ins_low = 0;
                ins_high = 0;
        }
        else if (ins_low > 4096)
        {
                ins.erase(ins.begin(), ins.begin() + ins_low);
                ins_high -= ins_low;
                ins_low = 0;
        }
        
        if (ins_high + 4096 >= ins.size()) ins.resize(ins_high + 4096);

        stream.async_read_some(boost::asio::buffer(ins.data()+ins_high,
                                                   ins.size()-ins_high),
                               boost::bind(&session::received_some,
                                           this, _1, _2));
        read_active = true;
}

Both helpers prune the buffers when necessary. (I should really remove those magic 4096s and make them a symbolic constant.)

The data members read_active and write_active ensure that at most one asynchronous read and at most one asynchronous write is pending at any given time. My first version did not have this safeguard (instead trying to rely on the ASIO stream reset method to cancel any outstanding asynchronous transput at need), and the code sent some TLS records twice – which is not good: sending the ServerHello twice is guaranteed to confuse the client.

Once ASIO completes an asynchronous transput request, it calls the corresponding handler:

void session::received_some(boost::system::error_code ec, size_t n)
{
        read_active = false;
        if (ec) { pending_error = ec; return; }
        ins_high += n;
        post_pending_actions();
}
void session::sent_some(boost::system::error_code ec, size_t n)
{
        write_active = false;
        if (ec) { pending_error = ec; return; }
        outs_low += n;
        post_pending_actions();
}

Their job is to update the bookkeeping and to trigger the resumption of suspended gnutls operations (which is done by post_pending_actions).

Now we have all the main pieces of the puzzle. The remaining pieces are obvious but rather messy, and I’d rather not repeat them here (not even in a cleaned-up form). But their essential idea goes as follows:

When called by the application code or when resumed by post_pending_actions, an asynchronous wrapper of a gnutls operation first examines the session state for a saved error code. If one is found, it is propagated to the application using the usual ASIO techniques, and the operation is cancelled. Otherwise, the wrapper calls the actual gnutls operation. When it returns, the wrapper examines the return value. If successful completion is indicated, the handler given by the application is posted in the ASIO io_service for later execution. If GNUTLS_E_AGAIN is indicated, post_read and post_write are called to schedule actual network transput, and the wrapper is suspended (by pushing it into a queue of pending actions). If any other kind of failure is indicated, it is propagated to the application using the usual ASIO techniques.

The post_pending_actions merely empties the queue of pending actions and schedules the actions that it found in the queue for resumption.

The code snippets above are not my actual working code. I have mainly removed from them some irrelevant details (mostly certain template parameters, debug logging and mutex handling). I don’t expect the snippets to compile. I expect I will be able to post my actual git repository to the web in a couple of days.

Please note that my (actual) code has received only rudimentary testing. I believe it is correct, but I won’t be surprised to find it contains bugs in the edge cases. I hope this is, still, of some use to somebody :)