Ohjelmointikielten periaatteet Syksy 2004 Antti-Juhani Kaijanaho
Copyright c 2002, 2004 Antti-Juhani Kaijanaho Tästä teoksesta saa valmistaa kappaleita ja sen saa saattaa yleisön saataviin, muuttamattomana tai muutettuna, käännöksenä tai muunnelmana, toisessa kirjallisuus- tai taidelajissa taikka toista tekotapaa käyttäen, mikäli kaikki seuraavat ehdot tulevat täytetyksi: (1) Kun teoksesta valmistetaan muutettu kappale tai muutettu teos kokonaan tai osittain saatetaan yleisön saataviin, on teoksen muuttajan nimi, salanimi tai nimimerkki ilmoitettava tekijän nimen rinnalla. (2) Kun teoksesta valmistetaan muutettu kappale tai muutettu teos kokonaan tai osittain saatetaan yleisön saataviin, on pidettävä huoli, ettei muutettua teosta voi hyvällä tahdolla sekoittaa alkuperäiseen. (3) Teoksen tekijän nimeä ei saa ilman hänen eri lupaansa käyttää teoksen markkinoinnissa. Erikseen korostettakoon, että teoksen tekijä ei vaadi maksua teoskappaleiden valmistamisesta tai teoksen saattamisesta yleisön saataviin. Teoskappaleen valmistaja tai teoksen yleisön saataviin saattaja saa periä tästä haluamansa maksun. Tekijänoikeuslaki (404/1961) 3 1 mom: Kun teoksesta valmistetaan kappale tai teos kokonaan tai osittain saatetaan yleisön saataviin, on tekijä ilmoitettava sillä tavoin kuin hyvä tapa vaatii.
Sisältö Esipuhe v Luku 1. Johdanto 1 1. Kielten jaotteluja 1 2. Kielen valinta 6 3. Ohjelmointikielten suunnitteluperiaatteita 7 4. Kielen määrittely 8 5. Toteutustekniikoista 10 Luku 2. Konekielinen ohjelmointi 11 1. Konekielet 11 2. Symboliset konekielet 13 3. Abstraktit koneet 14 4. Neloskielen idea 16 5. Systeemikutsut 21 6. Konekielellä ohjelmoinnista 22 Luku 3. Suoraviivaohjelmat 25 1. Suoraviivaohjelman elementit 25 2. Abstrakti syntaksi 33 3. Denotationaalinen semantiikka 36 4. Tyyppijärjestelmä 44 5. Konkreetti kielioppi 49 6. Toteutuksesta 55 Luku 4. Paikallinen kontrollivuon ohjaus 59 1. Kontrollivuo ja kontrollinohjaus 59 2. Testit 60 3. Valintalauseet 62 4. Toistolauseet eli silmukat 66 5. Vahtikomennot 71 6. Väitteet 73 Luku 5. Aliohjelmat 75 1. Kutsusekvenssit 75 2. Parametrinvälitysmekanismit 78 3. Staattinen ja dynaaminen vaikutusalue 79 4. Aktivaatiotietue 80 5. Vuorottaisrutiinit 82 iii
iv SISÄLTÖ 6. Ohjelmointi laskentana 82 Luku 6. Tyyppiteoriaa 91 1. Yksinkertaisesti tyypitetty λ-laskento 91 2. Alityypitys 100 3. Rekursiiviset tyypit 100 4. System F 100 5. Hindley Milner Damas -tyypitys 100 Luku 7. Samanaikaisuus 101 1. Jaettu muisti 101 2. Erilliset kommunikoivat prosessit 101 Luku 8. Abstraktit tietorakenteet ja modulit 103 Luku 9. Olioabstraktio 105 Kirjallisuutta 107 Liite A. Neloskoneen systeemikutsut 111 Liite B. Ohjelmointikielten historia 117 1. Kaksi ensimmäistä sukupolvea: ennen vuotta 1955 117 2. Automaattinen ohjelmointi ja ohjelmointikielten synty: 1955 1960 117 3. Baabelin torni: 1960-luku 119 4. Modernismi: 1970-luku 120 5. Postmodernismi: 1980-luku 121 6. Internetin nousu: 1990-luku 122 7. Sukupolvista 123 Liite C. Semanttiset alueet 125 1. Tuloalueet 127 2. Summa-alueet 128 3. Funktioalueet 129 Liite D. ALKEIS-suoran kääntäjä 133 1. Pääohjelma 133 2. Selain 137 3. Apuluokat 144 4. Lauseet 149 5. Tyypit 156 6. Lausekkeen jäsennin 163 7. Primäärilausekkeet 166 8. Operaattorilausekkeet 170 9. Muunnoslausekkeet 175
LUKU 6 Tyyppiteoriaa Tyyppiteoria on alun perin ollut logiikan ala, josta se on sittemmin laajentunut osaksi tietojenkäsittelyteoriaa, erityisesti tärkeäksi ohjelmointikielten teoriaa. Keskeinen perusteoria siinä on tyypitetty λ-laskento erilaisine laajennuksineen. Ohjelmointikielten näkökulmasta tyyppiteorian peruskirja on Piercen Types and Programming Languages [37]. 1. Yksinkertaisesti tyypitetty λ-laskento Yksinkertaisesti tyypitetyn λ-laskennon abstrakti syntaksi: ε ::=ι muuttuja ε 1 ε 2 applikaatio ν arvo τ ::=A perustyypit ν ::=ν A Γ ::= τ 1 τ 2 funktiotyyppi perustyypin vakio λι: τ.ε abstraktioarvo 1 tyhjä ympäristö Γ, ι : τ muuttujantyypitys Perustyyppien joukkoa merkitään A, joten A A on tosi. Kuten tyypittömässäkin λ-laskennossa, applikaatio assosioi vasemmalle. Hieman yllättäen vastaavasti funktiotyyppi assosioi oikealle: ε 1 ε 2 ε 3 = (ε 1 ε 2 )ε 3 mutta τ 1 τ 2 τ 3 = τ 1 (τ 2 τ 3 ). Operationaalinen semantiikka call by value -tyyliin: Tilat koostuvat pelkästään lausekkeista; lopputiloja ovat ne lausekkeet, jotka 1 Tarkkaan ottaen tämä on ns. heikko päänormaalimuoto (engl. Weak Head Normal Form) (WHNF). 91
92 6. TYYPPITEORIAA ovat arvoja. ε 1 ε 1 ε 1 ε 2 ε 1ε 2 ε ε νε νε (λι : τ.ε)ν = ε[ν/ι] Call by name (tilat samoja kuin edellä): ε 1 ε 1 ε 1 ε 2 ε 1ε 2 (λι : τ.ε 1 )ε 2 = ε 1 [ε 2 /ι] Tämä operationaalinen semantiikka ei tuota normaalimuotoja vaan heikkoja päänormaalimuotoja (engl. weak head normal form), lyhennetään WHNF. Lauseke on WHNF, jos se on vakio, muuttuja tai abstraktio. Päänormaalimuodolta (engl. head normal form, HNF) vaaditaan lisäksi, että jos se on abstraktio niin sen sisällä ei ole β-redeksejä (δ-redeksejä saa olla). WHNF riittää ohjelmointikielikäyttöön hyvin ja vastaa itse asiassa täysin ohjelmointikielten vakiintunutta semantiikkaa. Tyypityssäännöt ovat seuraavat: Γ ν A : A ι : τ Γ Γ ι : τ Γ, ι : τ 1 ε : τ 2 Γ (λι : τ 1.ε) : τ 1 τ 2 Γ ε 1 : τ 1 τ 2 Γ ε 2 : τ 1 Γ ε 1 ε 2 : τ 2 Tyyppiteorian tärkeä tehtävä on todistaa tyyppiturvallisuus (engl. type safety), Robin Milnerin sanoin well-typed programs cannot go wrong eli modernein sanakääntein: tyyppitarkastuksen hyväksymät ohjelmat eivät jumita (operationaalisen semantiikan abstraktia) konetta. Sivuutamme tämän asian tarkemman tarkastelun tässä; kiinnostuneiden kannattaa lukea edellä mainittu Piercen kirja [37].
1. YKSINKERTAISESTI TYYPITETTY λ-laskento 93 Kumitus (engl. erasure) määrittelee yhteyden tyypitetyn ja tyypittömän laskennon välille: erase(ι) = ι erase(λι : τ.ε) = λι. erase(ε) erase(ε 1 ε 2 ) = erase(ε 1 ) erase(ε 2 ) 1.1. Kiintopisteoperaattori. Yksinkertaisesti tyypitetyllä λ-laskennolla on se huomattava ominaisuus, että sen jokaisella lausekkeella on normaalimuoto. Huomattavaa se on siksi, että se osoittaa yksinkertaisesti tyypitetyn λ-laskennon olevan tyypitöntä λ-laskentoa heikompi: mitään kiintopisteoperaattoria ei yksinkertaisesti tyypitetyssä λ-laskennossa ole. Sen vuoksi sitä tavallisesti laajennetaan lisäämällä uusi lauseketyyppi: ε ::= fix ε Γ ε : τ τ Γ fix ε : τ ε ε fix ε fix ε fix(λx : τ.ε) ε[fix(λx : τ.ε)/x] erase(fix(ε)) = Y(erase(ε)), missä Y on Y-kombinaattori tai jokin muu tyypittömän λ-laskennon kiintopisteoperaattori. Mienkiintoista on, että kiintopisteoperaattorilla varustetussa yksinkertaisesti tyypitetyssä λ-laskennossa jokaiselle tyypille on olemassa ainakin yksi lauseke, joka on kyseistä tyyppiä. Yksi tällainen lauseke (mielivaltaiselle tyypille T) on fix(λy : T.y). Tällä lausekkeella ei ole normaalimuotoa, mutta sen tyyppi on silti T. Voidaan ajatella, että tämä lauseke edustaa tyypin T määrittelemätöntä lauseketta (engl. undefined expression) eli pohjaa. Ohjelmointikielissä, jotka rakentuvat tyypitetyn λ-laskennon päälle, on tavallisesti kielioppimakeisena (engl. syntactic sugar) 2 niinsanottu letrec-rakenne, joka mahdollistaa rekursiiviset määrittelyt. Se on 2 Syntactic sugar causes cancer of the semicolon. Alan Perlis [33].
94 6. TYYPPITEORIAA helppo määritellä kiintopisteoperaattorin avulla: letrec ι : τ = ε 1 in ε 2 (λι : τ.ε 2 ) fix(λι : τ.ε 1 ) 1.2. Yksikkötyyppi. Yksinkertaisesti tyypitetty λ-laskento soveltuu erinomaisesti ohjelmointikielen ytimeksi 3. Ohjelmointi raa alla λ-laskennolla on kuitenkin ohjelmoijalle raskasta ja hankala toteuttaa tehokkaasti; siksi sitä on tapana laajentaa. Ensimmäinen tällaisista ilmaisuvoiman kannalta epäolennaisista mutta käyttökelpoisuuden kannalta hyödyllisistä laajennuksista on yksikkötyyppi (engl. unit type), jossa on yksi arvo (normaalimuotoinen lauseke): τ ::= Unit ν ::= unit Γ unit : Unit Yksikkötyyppi vastaa C-sukuisten kielten void-tyyppiä, mutta toisin kuin void, Unit on täysivaltainen tyyppi ja siinä on arvo. Toteutus voi toki optimoida sen käytön kokonaan pois, sillä tyyppi, jossa on n eri arvoa, tarvitsee yhden arvon esittämiseen log 2 n bittiä, ja log 2 1 = 0. 1.3. Perustyypit. Sivuutetaan nyt lukutyyppien ja muiden sellaisten tuominen λ-laskentoon: ne eivät opeta meille mitään uutta. 3 Tällaisia ohjelmointikieliä sanotaan usein funktionaalisiksi kieliksi eli funktiokieliksi (engl. functional language), vaikka funktiokieli voi kyllä rakentua myös suoraviivaisen imperatiivisen ohjelmoinnin päälle.
1. YKSINKERTAISESTI TYYPITETTY λ-laskento 95 Sen sijaan hyödyllistä on laajentaa laskentoa vielä totuusarvoilla, vaikka ne voitaisiinkin esittää Churchin koodauksina: τ ::= Bool ν ::= true false ε ::= if ε 1 then ε 2 else ε 3 Γ true : Bool Γ false : Bool Γ ε 1 : Bool Γ ε 2 : τ Γ ε 3 : τ Γ if ε 1 then ε 2 else ε 3 : τ ε 1 ε 1 if ε 1 then ε 2 else ε 3 if ε 1 then ε 2 else ε 3 if true then ε 1 else ε 2 ε 1 if false then ε 1 else ε 2 ε 2 1.4. ALKEIS-while ja λ-laskento. Jos λ-laskennossa noudatetaan call by value -semantiikkaa, niin peräkkäistys voidaan saada aikaan seuraavasti: ε 1 ; ε 2 (λx : Unit.ε 2 ) ε 1, kunhan x ei esiinny ε 1 :ssä vapaana. Meillä on nyt riittävästi koneistoa sen osoittamiseen, että ALKEISwhilen keskeiset rakenteet on mahdollista kääntää edellä laajennettuun λ-laskentoon: T var ι 1 : τ 1 ; ; ι n : τ n begin κ end σ = T κ (σ {(ι 1, τ 1 ),..., (ι n, τ n )}) { (λι : τ.t κ σ) jos ε : τ ja (ι, τ ) σ T ι ε; κ σ = muuten T κ 1 ; κ 2 σ = T κ 1 σ;t κ 2 σ
96 6. TYYPPITEORIAA missä κ 1 ei ole sijoituslause T if ε then κ 1 else κ 2 σ = if ε then T κ 1 σ else T κ 2 σ T while ε do κ {(ι 1 : τ 1 ),..., (ι n, τ n )} = fix(λr, τ 1 τ n Unit. λι 1 : τ 1.....λι n : τ n.t κ ; r (ι 1 ( (ι n ) )) ) (ι 1 ( (ι n ) )), missä r on uusi muuttuja. Tässä muunnoksessa oletetaan yksinkertaisuuden vuoksi, että jokainen ALKEIS-whilen lauseke tai tyyppi on myös laajennetun λ-laskennon lauseke tai tyyppi. Ainoa keskeinen ALKEIS-whilen ominaisuus, joka ei tällä tekniikalla luonnistu suoraan, on päivitettävät taulukot. 1.5. Monikot. Monikko (engl. tuple) on tietorakenne, joka on muuten kiinteäkokoisen taulukon kaltainen, mutta sen kukin alkio voi olla omaa tyyppiään ja indeksi ei voi olla lauseke vaan sen on oltava vakio: τ ::= (τ 1,..., τ n ) ν ::= (ν 1,..., ν n ) ε ::= (ε 1,..., ε n ) ε 1 [k]
1. YKSINKERTAISESTI TYYPITETTY λ-laskento 97 k on kokonaislukuvakio Γ ε 1 : τ 1 Γ ε n : τ n Γ (ε 1,..., ε n ) : (τ 1,..., τ n ) Γ ε : (τ ) Γ ε : τ Γ ε : () Γ ε : Unit Γ ε : (τ 1,..., τ n ) Γ ε[k] : τ k (ν 1,..., ν n )[k] ν k ε ε ε[k] ε [k] ε k ε k (ν 1,..., ν k 1, ε k, ε k+11,..., ε n ) (ν 1,..., ν k 1, ε k, ε k+11,..., ε n ) (ε) ε () unit Tässä samastettiin 1-alkioinen monikko alkioonsa ja 0-alkioinen monikko yksikkötyypin arvoon. Kaksialkioista monikkoa sanotaan pariksi (engl. pair), kolmealkioista monikkoa kolmikoksi (engl. triplet), nelialkioista monikkoa nelikoksi (engl. quadruple), viisialkioista monikkoa viisikoksi (engl. quintuple), kuusialkioista monikkoa kuusikoksi (engl. sextuple) ja seitsenalkioista monikkoa seitsikoksi (engl. septuple). Enemmänkin nimiä on, mutta jo nelikkoa pidempien monikkojen nimet ovat harvinaisia. Monikkotyyppejä sanotaan toisinaan tulotyypeiksi (engl. product types). 1.6. Tietueet. Muistetaan aluksi, että monikko on kiinteäkokoinen taulukonomainen tietorakenne, jossa kukin alkio voi olla eri tyyppiä ja jossa alkioiden indeksin tulee olla käännösaikainen vakio.
98 6. TYYPPITEORIAA Tietue on monikon kaltainen vekotin, jossa alkioiden indeksien tilalla on alkioiden nimet: esimerkiksi {a = 4, b = true} on tietue, jonka tyyppi on {a : Int, b : Bool}. Toisin kuin useimmissa ohjelmolintikielissä tyyppiteoriassa ajatellaan tavallisesti, että tietueen kenttien järjestyksellä ei ole väliä vaan esimerkiksi tietuetyypit {a : Int, b : Bool} ja {b : Bool, a : Int} ovat sama tyyppi ja vastaavasti tietueet {a = 4, b = true} ja {b = true, a = 4} ovat sama tietue. Tietueen kentän arvo saadaan lausekkeella t.l, missä t on tietue ja l on kentän nimi. TEHTÄVÄ 6.1. Määrittele tietueet formaalisti. 1.7. Variantit. Variantti eli erillinen yhdiste (engl. tagged union) on tietueen kaltainen vekotin, joka eroaa tietueesta siinä, että se tallentaa vain yhden kentän arvoa kerrallaan. Esimerkiksi varianttityyppiin a : Int, b : Bool kuuluvat muiden muassa varianttiarvot a = 4 ja b = true. Sen sijaan varianttiarvoa a = 4, b = true ei ole olemassakaan! Varianttityypeille kuuluu sama tyyppien samuussääntö kuin tietuetyypeille: kenttien järjestyksellä ei ole merkitystä. Variantin kentän arvon saaminen ei ole yhtä helppoa kuin tietueissa. Periaatteessa ohjelmoijan tulee varautua siihen, että variantti voi sisältää mitä vain tyyppinsä sisältämää dataa. Tämän vuoksi järkevin rakenne variantin arvon saamiseksi on case-lauseke: case v of a = x x + 2 b = x not x Jos tässä lausekkeessa v:n arvo on a = 4, niin lausekkeen arvo on 6; jos taas v:n arvo on b = true, niin lausekkeen arvo on false. TEHTÄVÄ 6.2. Määrittele variantit formaalisti. Variantit ovat läheistä sukua summatyypeille (engl. sum type). Summatyyppi voidaan käsittää varianttityypiksi, jossa kunkin kentän nimi on kyseisen kentän tyypin nimi ja kahdella kentällä ei ole samaa tyyppiä. 1.8. Algebralliset tietotyypit. Haskellissa voidaan kirjoittaa seuraavasti: data Lauseke = Plus Lauseke Lauseke Miinus Lauseke Lauseke Kerto Lauseke Lauseke Jako Lauseke Lauseke Jaannos Lauseke Lauseke Kokonaisluku Integer Liukuluku Double Tämä määrittelee tyypin nimeltä Lauseke. Määrittelyn vaikutusalueella voidaan nimiä Plus, Miinus, Kerto, Jako, Jaannos, Kokonaisluku
1. YKSINKERTAISESTI TYYPITETTY λ-laskento 99 ja Liukuluku käyttää koostinfunktioina (engl. constructor), joista kaksi viimeistä ottavat yhden parametrin (Kokonaisluku ottaa parametrin tyyppiä Integer, Liukuluku ottaa Doublen) ja loput ottavat kaksi Lauseke-tyyppistä parametria. Tässä määritelty tyyppi toimii vastaavasti kuin λ-laskennon monikoita ja variantteja käyttäen esitelty tyyppi Plus : (Lauseke, Lauseke), Miinus : (Lauseke, Lauseke), Kerto : (Lauseke, Lauseke), Jako : (Lauseke, Lauseke), Jaannos : (Lauseke, Lauseke), Kokonaisluku : Integer, Liukuluku : Double Lauseke-parametrin ottavia funktioita voidaan Haskellissa määritellä paloittain hahmontunnistuksella siten, että parametrin paikalle kirjoitetaan jokin koostimista ja sen parametreiksi nimiä (tai sitten muita samanlaisia hahmoja): sievenna :: Lauseke -> Lauseke sievenna (Plus (Kokonaisluku i) (Kokonaisluku j)) = Kokonaisluku (i + j) sievenna (Miinus (Kokonaisluku i) (Kokonaisluku j)) = Kokonaisluku (i - j) sievenna (Kerto (Kokonaisluku i) (Kokonaisluku j)) = Kokonaisluku (i * j)... Tyyppejä, (1) jotka ovat oleellisesti monikoita sisältäviä variantteja, joissa variantin kenttien nimet voidaan valita vapaasti, (2) joiden kentännimet toimivat koostinfunktioina lausekkeissa, ja (3) joiden arvoja voidaan sovittaa kentännimistä ja muista hahmoista koostettuun hahmoon, kun funktioita määritellään paloittain, sanotaan algebrallisiksi (algebraic types). Algebrallisia tietotyyppejä on nykyisin lähinnä sellaisissa kielissä kuin ML, Haskell ja Pizza 4. Algebralliset tyypit ovat luonnostaan rekursiivisia: on mahdollista kirjoittaa tyypinmäärittely, jossa määriteltävä tyyppi itse esiintyy koostimen parametrityyppinä. Tätä ominaisuutta tarvitaan useimpien kehittyneiden tietorakenteiden (kuten puut ja verkot) toteuttamiseen. 4 Ks. http://pizzacompiler.sourceforge.net/
100 6. TYYPPITEORIAA 1.9. Viitteet. 2. Alityypitys Alityypityksessä oleellista on subsumptiosääntö: Jos tyyppi τ 1 on tyypin τ 2 alityyppi (merkitään τ 1 <: τ 2 ), niin kaikki τ 1 :n arvot käyvät τ 2 :n arvoista ja τ 1 - tyyppistä lauseketta voidaan käyttää missä vain missä τ 2 -tyyppistä lauseketta voidaan käyttää. 3. Rekursiiviset tyypit Rekursiiviset tyypit mahdollistavat mielivaltaisen kokoisten tietorakenteiden tyypittämisen. FIXME 4. System F Geneerisen polymorfismin perusteoria. FIXME 5. Hindley Milner Damas -tyypitys Let-polymorfismin perustekniikka. FIXME
Kirjallisuutta [1] IEEE standard for binary floating-point arithmetic, 1985. ANSI/IEEE Std 754-1985. [2] 7-Bit coded Character Set, 1991. Standard ECMA 6. WWW: http://www.ecma. ch/ecma1/stand/ecma-006.htm. [3] Addison-Wesley, Reading, MA. The Unicode Standard, Version 4.0, 2003. [4] Alfred V. Aho, Ravi Sethi, ja Jeffrey D. Ullman. Compilers: Principles, Techniques and Tools. Addison-Wesley, Reading, MA, 1988. [5] Andrew W. Appel. Compiling with Continuations. Cambridge University Press, Cambridge, 1992. [6] Andrew W. Appel ja Jens Palsberg. Modern Compiler Implementation in Java. Cambridge University Press, Cambridge, 2. laitos, 2002. [7] John Backus. Can programming be liberated from the von Neumann style? A functional style and its algebra of programs. Communications of the ACM, 21(8), Elokuu 1978. [8] C. J. Burgess. Software quality issues when choosing a programming language. Kirjassa Software Quality Management III Vol. 2, sivua 25 31. Computational Mechanics Publications, 1995. [9] O.-J. Dahl, E. W. Dijkstra, ja C. A. R. Hoare. Structured Programming. Academic Press, Lontoo, 1972. [10] Edsger W. Dijkstra. Go to statement considered harmful. Communications of the ACM, 11(3), Maaliskuu 1968. Letter to the editor. [11] Edsger W. Dijkstra. A Discipline of Programming. Prentice-Hall, Englewood Cliffs, NJ, 1976. [12] Erich Gamma, Richard Helm, Ralph Johnson, ja John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, Boston, 1995. [13] C. A. R. Hoare. Hints on programming language design. Kirjassa C. A. R. Hoare ja C. B. Jones (toim.), Essays in Computer Science. Prentice-Hall, New York, 1989. [14] C. A. R. Hoare. A hard act to follow. Higher-Order and Symbolic Computation, 13(1 2):71 72, 2000. [15] C. A. R. Hoare. Assertions: a personal perspective. IEEE Annals of the History of Computing, 25(2), 2003. [16] Tony Hoare. Towards the verifying compiler. Kirjassa Olaf Owe, Stein Krogdahl, ja Tom Lyche (toim.), From Object-Orientation to Formal Methods: Essays in Memory of Ole-Johan Dahl, nide 2635 sarjasta Lecture Notes in Computer Science. Springer, 2004. [17] International Organization for Standardization. Information lechnology Syntactic metalanguage Extended BNF, 1996. ISO/IEC 14977:1996(E). [18] International Organization for Standardization. Programming languages C, 1999. ISO/IEC 9899:1999. [19] Antti-Juhani Kaijanaho ja Benjamin Fallenstein. Totally different structural programming: Programming languages in ZigZag. WWW: http://www.mit. 107
108 KIRJALLISUUTTA jyu./antkaij/plinzz.pdf, Elokuu 2001. An invited talk presented at the First International ZigZag Conference, part of ACM Hypertext Conference 2001 in Århus, Denmark on August 14, 2001. [20] Richard Kelsey, William Klinger, Jonathan Rees, et al. Revised 5 report on the algorithmic language Scheme. ACM SIGPLAN Notices, 33(9):26 76, Syyskuu 1998. [21] Heiko Kießling ja Uwe Krüger. Blocks and procedures. SIGPLAN Notices, 28(11), 1993. [22] Donald E. Knuth. Backus normal form vs. backus naur form. Communications of the ACM, 7(12), Joulukuu 1962. Letter to the editor. [23] Donald E. Knuth. Structured programming with go to statements. Computing Surveys, 6(4), Joulukuu 1974. [24] Donald E. Knuth ja Luis Trabb Pardo. The early development of programming languages. Kirjassa J. Belzer, A. G. Holzman, ja A. Kent (toim.), Encyclopedia of Computer Science and Technology, nide 6. Dekker, New York, 1977. Julkaistu myös kirjassa [28]. [25] Thomas E. Kurtz. BASIC. ACM SIGPLAN Notices, 13(8):103 118, Elokuu 1978. Preprints, First ACM SIGPLAN History of Programming Languages Conference. [26] Patricia K. Lawlis. Guidelines for choosing a computer language: Support for the visionary organization, Elokuu 1997. [27] John McCarthy. History of LISP. ACM SIGPLAN Notices, 13(8):217 223, Elokuu 1978. Preprints, First ACM SIGPLAN History of Programming Languages Conference. [28] N. Metropolis, J. Howlett, ja Gian-Carlo Rota (toim.). A History of Computing in the Twentieth Century. Academic Press, New York, 1980. [29] Robin Milner. A theory of type polymorphism in programming. Journal of Computer and System Sciences, 17(3):348 375, 1978. [30] Hanspeter Mössenböck. Treating statement sequences as block objects. SIGPLAN Notices, 27(8), 1992. [31] Peter Naur et al. Revised report on the algorithmic language ALGOL 60. Communications of the ACM, 6(1):1 17, Tammikuu 1963. [32] John K. Ousterhout. Scripting: Higher-level programming for the 21st century. Computer, 31(3):23 30, Maaliskuu 1998. [33] Alan J. Perlis. Epigrams on programming. ACM SIGPLAN Notices, 17(9), Syyskuu 1982. [34] Simon Peyton Jones (toim.). Haskell 98 Language and Libraries: The Revised Report. Cambridge University Press, Huhtikuu 2003. [35] Simon Peyton Jones. Haskell 98 language and libraries: The revised report. Journal of Functional Programming, 13(1), Tammikuu 2003. [36] Simon L. Peyton Jones. The Implementation of Functional Programming Languages. Prentice-Hall, New York, 1987. [37] Benjamin C. Pierce. Type and Programming Languages. MIT Press, Cambridge, MA, 2002. [38] Eric S. Raymond. The Art of Unix Programming. Addison-Wesley, Boston, 2003. [39] John C. Reynolds. Theories of Programming Languages. Cambridge University Press, Cambridge, 1998. [40] Guy Lewis Steele, Jr. Debunking the expensive procedure call myth or procedure call implementations considered harmful or, LAMBDA: The ultimate GOTO. Kirjassa James K. Ketchel et al. (toim.), Proceedings of the 1977 annual conference, sivua 153 162. ACM Press, 1977.
KIRJALLISUUTTA 109 [41] Christopher Strachey. Fundamental concepts in programming languages. Higher-Order and Symbolic Computation, 13:11 49, 2000. Perustuu Stracheyn vuonna 1967 pitämiin luentoihin. [42] A. van Wijngaarden et al. (toim.). Revised Report on the Algorithmic Language Algol 68. Springer, Berliini, 1976. [43] Larry Wall. Perl, the first postmodern computer language. http://www.wall. org/ larry/pm.html, 1999. A talk given in Linux World Spring 1999.
LIITE B Ohjelmointikielten historia Tässä liitteessä esitetään ohjelmointikielten historian päävaiheet. 1. Kaksi ensimmäistä sukupolvea: ennen vuotta 1955 Ensimmäiset ohjelmointikielet olivat konekieliä ja niillä kirjoitetut ohjelmat olivat bittijonoja tietokoneen muistissa. Ohjelmat syötettiin koneelle kytkimiä kääntelemällä. Konekieli on ratkaisuavaruuden sydämessä: konekielellä ei ole kääntäjää tai tulkkia, joka auttaisi ihmistä ongelman tuomisessa ratkaisuavaruuteen. Ohjelmien kirjoittaminen konekielellä on erittäin virhealtista ja toisaalta hyvin mekaanista puuhaa. Jo hyvin varhaisessa vaiheessa alettiin kirjoittaa ohjelmia, jotka auttoivat tässä prosessissa. Ihmiset kirjoittivat sopivalla tavalla muotoiltua tekstiä, ja nämä ohjelmat muunsivat sen konekieleksi. Tyypillisesti tässä syötteessä yksi rivi vastaa yhtä konekielen käskyä. Tätä ohjelmointikieltä kutsutaan symboliseksi konekieleksi (engl. assembler language), ja ohjelmaa, joka kääntää tällä kirjoitetun ohjelman konekielellä, sanotaan assembleriksi. Konekielet muodostavat ohjelmointikielten ensimmäisen sukupolven ja symboliset konekielet ohjelmointikielten toisen sukupolven. Ongelmana näissä on ohjelmoinnin virhealttius ja vahva mekaanisuus sekä se, että jokaisella konetyypillä on oma konekielensä ja sitä vastaava symbolinen konekieli. Ohjelmia ei voitu siirtää konetyyppien välillä, vaan ne jouduttiin kirjoittamaan uudestaan aina kullekin uudelle konetyypille. 2. Automaattinen ohjelmointi ja ohjelmointikielten synty: 1955 1960 Ongelman ratkaisuksi esitettiin automaattista ohjelmointia, joksi sitä silloin 1950-luvun alkupuolella sanottiin. Kyse oli siitä, että ohjelmoija esittää tietokoneelle ongelman kuvauksen jollakin formaalilla kielellä, ja jokin erityinen ohjelma tuottaa siitä konekielisen ohjelman, joka ratkaisee ongelman. Eivät ne formaalit kielet mitenkään kummoisia olleet, mutta senaikaiseen tietämykseen nähden ne 117
118 B. OHJELMOINTIKIELTEN HISTORIA olivat tekoälytutkimuksen (josta ei tuota nimeä vielä käytetty) huippua. Nykyään näitä formaaleja kieliä sanotaan korkean tason tai kolmannen sukupolven ohjelmointikieliksi ja ohjelmia, jotka tekevät niillä kirjoitetuista ongelmakuvauksista konekielisiä ohjelmia, sanotaan kääntäjiksi (engl. compiler). Näitä varhaisia kieliä on esitelty Knuthin ja Trabb Pardon artikkelissa vuodelta 1977 [24]. Automaattiseen ohjelmointiin eivät useimmat ihmiset uskoneet, ennen kuin Fortran tuli kuvioihin 1950-luvun lopulla ensimmäinen versio julkaistiin keväällä 1957, mutta kielestä oli siinä vaiheessa puhuttu jo monta vuotta. Fortran oli ensimmäinen merkittävää kannatusta saavuttanut korkean tason ohjelmointikieli (ja kiinnostavaa kyllä, edelleen laajassa käytössä, tosin reilusti muuttuneena). Ensimmäinen Fortran-kääntäjä oli vaikea tehtävä: sitä tehtiin kolme vuotta ja siihen kului 18 ihmistyövuotta. Tätä työtä johti John Backus, joka on myöhemminkin ollut merkittävä tekijä ohjelmointikieliteknologian alalla. Fortanin merkitys ohjelmointikielten kehitykselle on kiistaton. Se oli alan pioneerityö: se sai ihmiset uskomaan korkean tason ohjelmointikieliin ylipäätään. Lisäksi monet sen ominaisuudet ovat edelleen ohjelmointikielissä tärkeitä: taulukkomuuttujat, muuttujan ohjaamat silmukat (ns. for-silmukat) sekä haarautuva if-lause (if statement) ovat nykykielissäkin peruskamaa. Fortran oli tarkoitettu tieteellisen laskennan ohjelmien kirjoittamiseen. John McCarthy, tekoälytutkimuksen kantaisä, halusi kielen symbolista tiedonkäsittelyä (esimerkiksi algebrallisten funktioiden symbolista derivointia) varten. McCarthy ja muutamat muut laajensivat Fortrania aluksi symbolista laskentaa varten, mutta tämän rajat tulivat pian vastaan. He aloittivat uuden kielen, Lispin, kehittämisen ensin paperilla ja käsikääntäen sillä kirjoitettuja ohjelmia paperille. Kun sitten huomattiin, että teoreettiseksi leluksi tarkoitettu Lisp-funktio eval toimii mainiosti Lisp-tulkkina, oli Lisp-toteutus valmis [27]. Fortranin tapaan Lispin merkitys myöhemmille ohjelmointikielille on merkittävä. Lispin mukana näkivät päivänvalon mm. rekursiiviset aliohjelmat, muistinsiivous (engl. garbage collection) ja if-lauseke (if expression). Nykyisin Lispistä on käytössä kolme merkittävää murretta: Emacs Lisp, Common Lisp ja Scheme. Algoritmien julkaisemiseen kehitettiin komiteatyönä 1950-luvun lopulla uusi kieli, Algol (tämän sanotaan usein olevan lyhenne sanoista Algorithmic language, mutta se viittaa myös samannimiseen kolmoistähteen). Algol 60 [31] oli kolmas 1950-luvun loppupuolella kehitetty kieli, jolla on ollut merkittävää merkitystä ohjelmointikielten tulevalle kehitykselle. Algol 60 oli ensimmäinen lohkorakenteinen kieli. Sillä oli vapaamuotoinen, rakenteinen syntaksi.
3. BAABELIN TORNI: 1960-LUKU 119 Siinä oli muuttujien tyyppien esittelyt ja arvovälitteiset (engl. call-byvalue) aliohjelmaparametrit. Nämäkin kaikki ovat lähes välttämättömiä nykykielissä. Tony Hoare kirjoitti Algol 60:stä vuonna 1973 seuraavasti: [13] Tässä on kieli, joka on niin edellä aikaansa, että se oli paitsi edeltäjiään myös lähes kaikkia seuraajiaan parempi. 1950-luvun lopulla kehitettiin myös kaksi muuta suosiota saavuttanutta kieltä, Cobol ja APL. Kummankin merkitys ohjelmointikielten kehitykselle kokonaisuutena on kuitenkin vähäinen, joten niihin ei tämän enempää puututa. 3. Baabelin torni: 1960-luku 1960-luvulla suunniteltiin satoja uusia ohjelmointikieliä, joista hyvin harva jäi eloon. Nimet Adam, AED, Altran, CLP, Dyana ja Unicode eivät nykyään liity ohjelmointikieliin lainkaan mutta ne kaikki olivat vuonna 1967 käytössä olleita ohjelmointikieliä. Monet näistä kielistä oli tarkoitettu johonkin erityiseen tarkoitukseen. Algol-perhe sai uuden jäsenen, Algol 68:n, joka kokonsa ja vaikealukuisen määrittelynsä [42] vuoksi jäi pääosin käyttämättä. Samoihin aikoihin Niklaus Wirth ja Tony Hoare suunnittelivat oman seuraajansa Algol 60:lle, jota on kutsuttu Algol W:ksi. Norjalaiset Kristen Nygaard ja Ole-Johan Dahl kehittivät Simulakielen simulointien kirjoittamiseen. Simulan merkitys nykykielille on ollut valtava, samaa suuruusluokkaa kuin Algol-perheen ja Lispin: Simulasta alkoi olio-ohjelmoinnin perinne jo Simula I:n yhteydessä puhuttiin olioista ja luokista. Perintä lisättiin Simula 67:ään. David Farber, Ralph Griswold ja Ivan Polonsky kehittivät Snobolkielen merkkijonojen käsittelyä varten 1962. Snobol vaikutti suuresti tulevien kielten (esimerkiksi Perlin) merkkijono-ominaisuuksiin. 1960-luvun lapsia on myös BASIC. Dartmouth Collegessa haluttiin luoda tietokonejärjestelmä ja sille ohjelmointikieli, jotka ovat niin helppoja, että niiden käyttöä voitaisiin opettaa kaikille humanisteille. BASIC sai monia vaikutteita Algol 60:stä, mutta jätti monta (näin jälkiviisaasti sanoen: hyvin tärkeätä) sen ominaisuutta pois. Esimerkiksi lohkorakenne tiputettiin pois, koska sitä ei haluttu selittää humanisteille [25]. IBM:n ja muun teollisuuden yhteistyönä kehitettiin 60-luvulla PL/I (oikeastaan PL/1, mutta ykkönen on tapana tässä tapauksessa kirjoittaa isona I-kirjaimena), joka oli yritys luoda eräänlainen universaali ohjelmointikieli. Aiemmin oli ohjelmointitehtävät ja ohjelmoijat jakautuneet kahteen selvään ryhmään: tieteellinen laskenta, jossa
120 B. OHJELMOINTIKIELTEN HISTORIA tarvittiin liukulukulaskentaa, taulukoita ja aliohjelmia, sekä hallinnollinen tietojenkäsittely, jossa tarvittiin kiintolukulaskentaa (engl. fixedpoint arithmetic), tehokasta siirräntää (engl. input/output) ja merkkijonojen käsittelyä. 1960-luvulla tämä jako alkoi hämärtyä, ja eräs PL/I:n oleellisimmista tavoitteista oli toimia molempien ryhmien ohjelmointikielenä. Kieli oli 60- ja 70-luvuilla varsin suosittu ja IBM tukee sitä edelleen, mutta nykyisin sitä pidetään (yhdessä Algol 68:n kanssa) lähinnä varoittavana esimerkkinä laajasta ja vaikeasta kielestä. PL/I oli ensimmäinen ohjelmointikieli, joilla on muodollisesti (PL/I:n tapauksessa VDM:llä) määritelty merkitysoppi (engl. semantics). 4. Modernismi: 1970-luku 1970-luvulla alettiin rakentaa edellisen vuosikymmenen Baabelin tornin raunioiden päälle. Tärkeinä lähtökohtina pidettiin kielen yksinkertaisuutta ja johdonmukaisuutta. Samaan aikaan puhuttiin paljon rakenteisesta ohjelmoinnista [9] (engl. structured programming), ja monet 70-luvulla suunnitelluista kielistä oli nimenomaisesti suunniteltu rakenteista ohjelmointia ajatellen. 1970-luvun kielet ovat ensimmäisiä, joita voi kutsua moderneiksi. Edellisinä kahtena vuosikymmenenä oli kehitetty niin paljon kieliä, että niiden virheistä saattoi oppia jotain merkittävää. 1970-luvun jälkeen on myöskin ilmaantunut hyvin vähän radikaaleja, uusia ideoita ohjelmointikielten alalla kielet ovat kyllä kehittyneet mutta vähittäin. Niklaus Wirth ei todellakaan pitänyt Algol 68:sta. Hän alkoi suunnitella uutta kieltä Algol-perinteen sisällä aiemman Algol W:nsä pohjalta. Tulos oli Pascal, toisaalta opetukseen mutta toisaalta myös käytännön ohjelmointityöhön tarkoitettu kieli. Pascalin eräs varhainen, aikoinaan suosittu toteutus perustui samantyyppiselle virtuaalikoneratkaisulle kuin Java nykyisin. Toinen Algol-perinteeseen kytkeytynyt uusi kieli oli Dennis Ritchien C. C oli tarkoitettu raakaan järjestelmäohjelmointiin: sillä oli tarkoitus kirjoittaa (ja kirjoitettiinkin) UNIX kokonaan uudestaan. C:n merkitystä myöhemmille kielille on vaikea yliarvioida, vaikka se sinänsä ei sisältänytkään juuri mitään kieliteknologista uutta sen merkittävyys johtuu siitä, että se on erittäin suosittu. Olio-ohjelmointi ei vielä 1970-luvulla ollut kovin merkittävässä asemassa. Xeroxin PARC-laboratorioissa kehitettiin WIMP-käyttöliittymää, ja merkittävä osa tätä kehitystyötä oli Smalltalk-kielen kehitys. Smalltalk on edelleen yksi puhtaimmista oliokielistä. Itse kieli on hyvin pieni, mutta sen peruskirjasto on järkyttävän suuri.
5. POSTMODERNISMI: 1980-LUKU 121 Funktio-ohjelmointi oli 1970-luvulla rajoittunut lähinnä Lisp-sukuisten kielten käyttöön. Robin Milner kehitti Edinburghin yliopiston päättelyavustimen käyttöön metakielen (kielen, jolla kuvataan kieliä), jota hän kutsui ML:ksi (lyhenne sanasta meta-language). Sen tapa ajatella ohjelmia sai paljon vaikutteita Lispistä mutta sen syntaksi on Algol-tyyppinen. ML:n ehkä suurin merkitys ohjelmointikielten kehitykselle on Hindley Milner-tyyppijärjestelmä [29], jota mm. Generic Java käyttää tietyin muunnoksin. Samoihin aikoihin Guy Lewis Steele, Jr. ja Gerald Jay Sussman kehittivät uutta Lisp-varianttia, jonka he nimesivät Schemeksi. Scheme käytti Lispin omintakeista Cambridgen-puolalaista [27] syntaksia mutta otti käyttöön Algoleista tutun lohkorakenteisuuden. Schemen kehittäjät olivat vahvasti sitä mieltä, että aliohjelmakutsun ei todellakaan tarvitse olla hidas operaatio [40], ja he suunnittelivat kielensä (ja sen toteutukset) tuon käsityksen ympärille. Schemen määrittelyyn kuuluu edelleen vaatimus häntäkutsun poistamisesta, mitä juuri mikään muu kieli ei nykyisin vaadi, vaikka syytä olisi. 1970-luvun alussa syntyi ohjelmointikieli nimeltään Prolog, joka oli täysin erilainen kuin aiemmat kielet, eikä nykyisistäkään kielistä kovin monta samantyyppistä löydy. Prologin perusidea on predikaattilogiikan kaavojen tulkitseminen proseduraalisesti siten, että ohjelma koostuu kaavoista, jotka tulkitaan aliohjelmiksi, ja jonkin näistä aliohjelmista kutsuminen vastaa sitä vastaavan kaavan todistamista muiden toimiessa aksioomina. 5. Postmodernismi: 1980-luku 1980-lukua voi hyvällä syyllä sanoa olio-ohjelmoinnin popularisoinnin vuosikymmeneksi. Tällä vuosikymmenellä kehitettiin monta merkittävää olio-ohjelmointiin käytettyä kieltä. Samalla myös funktioohjelmoinnin tutkimus eteni, ja vaikka funktio-ohjelmointi ei saavuttanutkaan samaa suosiota kuin olio-ohjelmointi, myös sen kannalta merkittäviä kieliä kehitettiin. 1980-luvun alussa alkoi Bjarne Stroustrup kehitellä oliolaajennusta Ritchien C-kieleen. Hän kutsui tuota kieltä nimellä C with classes, mutta varsin pian nimi vaihtui C++:ksi. Vaikka C++ alkoikin oliokielenä, on siihen myöhemmin lisätty paljon muitakin ohjelmointityylejä tukevia ominaisuuksia esimerkiksi C++:n tyyppijärjestelmästä kehittyi oma funktiopohjainen ohjelmointikielensä C++:n sisälle! Toinen tunnettu 1980-luvulla kehitetty oliokieli on Bertrand Meyerin Eiffel. Se on puhdas oliokieli ja sisältää myös tukea ohjelmien formaalille verifioinnille (esi- ja jälkiehdot sekä luokkainvariantit on mahdollista esitellä ja tarkastaa ajoaikana samoja ominaisuuksia löytyi muutamasta aiemmasta kielestä kuten Alphard ja Euclid).
122 B. OHJELMOINTIKIELTEN HISTORIA 1980-luvulla saatiin valmiiksi Yhdysvaltain puolustusministeriön toimeksiannosta kehitetty kieli Ada (Augusta Ada Byronin, Lovelacen kreivittären mukaan, jonka on sanottu olleen maailman ensimmäinen ohjelmoija). Adan tärkeimpiä sovelluskohteita olivat alunperin sulautetut järjestelmät ja tosiaikaohjelmistot, erityisesti asejärjestelmät. David Turner kehitti funktiokielen Miranda 1980-luvun puolessa välissä. Se muistutti monessa suhteessa ML:ää, mutta se käytti johdonmukaisesti laiskaa laskentaa (tähän palataan vielä) ja oli puhdas funktiokieli. Mirandan käyttöä rajoitti Turnerin tapa pitää kielestään voimakkaasti kiinni: hän jopa patentoi osia siitä. Samantyyppisiä omia kieliä oli lähes jokaisella funktio-ohjelmoinnista kiinnostuneella tutkijalla, joten vähän myöhemmin perustettiin komitea suunnittelemaan uutta Mirandan kaltaista ohjelmointikieltä. 1980-luvun loppupuolella syntyi kaksi merkittävää juontokieltä (engl. scripting languages): John Ousterhoutin Tcl ja Larry Wallin Perl. Tcl:n pääideana oli olla kieli, jonka voisi upottaa ohjelmistoihin, Perl syntyi tarpeesta käsitellä tekstitiedostoja mitä erilaisimmin tavoin. Larry Wall [43] kuvaa Perliä maailman ensimmäiseksi postmodernistiseksi ohjelmointikieleksi. Eräässä mielessä koko 1980-lukua voisi kuvailla postmodernismin vuosikymmeneksi. 6. Internetin nousu: 1990-luku Vuonna 1993 alkoi Internetin ikuinen syyskuu, kun Internet alkoi vähitellen nousta tavallisten ihmisten tietoisuuteen. Samalla ohjelmoinnin fokus siirtyi kohti verkkosovelluksia. James Gosling työryhmineen kehitti Java-kielen, vuonna 1995 julkaistun C-sukuisen oliokielen, joka on niin konservatiivisesti suunniteltu, ettei siinä ole juuri mitään uutta. Javan, kuten muidenkin C-sukuisten kielten, merkitys on sen suosikkiasemassa. Aivan 1990-luvun alussa sai edellisellä vuosikymmenellä muodostettu funktiokielikomitea työnsä päätökseen. Tulos oli Haskell, jonka tarkoitus on olla se oikea laiskasti laskeva, puhdas funktiokieli. Tämä tavoite onkin onnistunut. Vuonna 1999 valmistui kielen nykyinen määrittely, Haskell 98. Haskell näyttäisi ratkaisseen funktiokieliä vaivanneen ongelman siirrännästä varsin omintakeisella monadisen tietotyypin käsitteellään. Jo 1980-luvulla alkanut juontokielten kehitys jatkui 1990-luvulla. Guido van Rossum kehitti Pythonin vuosikymmenen alussa, Yukihiro Matsumoto kehitti Rubyn. 90-luvun lapsia ovat myös PHP ja ECMAScript (tunnetaan myös nimellä Javascript).
7. SUKUPOLVISTA 123 7. Sukupolvista Olen edellä viitannut ohjelmointikielten ensimmäiseen, toiseen ja kolmanteen sukupolveen. Kukaan ei näytä tietävän, mistä koko sukupolvijaottelu on peräisin, ja lähteet määrittelevät sukupolvet kukin omalla tavallaan. Edellä esittelemäni määritelmät kolmelle ensimmäiselle sukupolvelle ovat jonkinlainen lähteiden konsensus, vaikka poikkeaviakin esityksiä olen nähnyt. Useimmat lähteet puhuvat myös neljännestä ja viidennestä sukupolvesta, mutta mitään yksimielisyyttä niiden sisällöstä ei ole.