Tyyppipäättely. Keijo Mattila Tiivistelmä

Samankaltaiset tiedostot
Haskell ohjelmointikielen tyyppijärjestelmä

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

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

Geneeriset tyypit. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos

Ydin-Haskell Tiivismoniste

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Taas laskin. TIES341 Funktio ohjelmointi 2 Kevät 2006

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

13. Loogiset operaatiot 13.1

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

13. Loogiset operaatiot 13.1

TIES542 kevät 2009 Rekursiiviset tyypit

Demo 7 ( ) Antti-Juhani Kaijanaho. 9. joulukuuta 2005

TIEA341 Funktio-ohjelmointi 1, kevät 2008

ITKP102 Ohjelmointi 1 (6 op)

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Java-kielen perusteet

Rekursiiviset tyypit

11/20: Konepelti auki

Yksinkertaiset tyypit

TIES542 kevät 2009 Tyyppiteorian alkeet

811120P Diskreetit rakenteet

Ohjelmoinnin peruskurssien laaja oppimäärä

Osoitin ja viittaus C++:ssa

Oliot ja tyypit. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos

Ohjelmoinnin perusteet Y Python

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

TIE Principles of Programming Languages CEYLON

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

Ohjelmoinnin peruskurssien laaja oppimäärä

Java-kielen perusteita

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssien laaja oppimäärä

Common Lisp Object System

4. Luokan testaus ja käyttö olion kautta 4.1

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

Ohjelmoinnin peruskurssien laaja oppimäärä

16. Ohjelmoinnin tekniikkaa 16.1

T Syksy 2002 Tietojenkäsittelyteorian perusteet Harjoitus 8 Demonstraatiotehtävien ratkaisut

Sisällys. 17. Ohjelmoinnin tekniikkaa. Aritmetiikkaa toisin merkiten. for-lause lyhemmin

Luku 3. Listankäsittelyä. 3.1 Listat

Sisällys. 16. Ohjelmoinnin tekniikkaa. Aritmetiikkaa toisin merkiten. Aritmetiikkaa toisin merkiten

Laajennetaan vielä Ydin-Haskellia ymmärtämään vakiomäärittelyt. Määrittely on muotoa

Logiikan kertausta. TIE303 Formaalit menetelmät, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos.

16. Ohjelmoinnin tekniikkaa 16.1

Tyyppejä ja vähän muutakin. TIEA341 Funktio ohjelmointi 1 Syksy 2005

12. Monimuotoisuus 12.1

TIES542 kevät 2009 Tyyppijärjestelmän laajennoksia

14.1 Rekursio tyypitetyssä lambda-kielessä

Olio-ohjelmointi Javalla

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

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

Ohjelmoinnin perusteet Y Python

TIEA341 Funktio-ohjelmointi 1, kevät 2008

1.3Lohkorakenne muodostetaan käyttämällä a) puolipistettä b) aaltosulkeita c) BEGIN ja END lausekkeita d) sisennystä

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2

Ohjelmoinnin peruskurssien laaja oppimäärä

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

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Ohjelmoinnin peruskurssien laaja oppimäärä

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin peruskurssien laaja oppimäärä

Rajoittamattomat kieliopit (Unrestricted Grammars)

Rajapinta (interface)

5/20: Algoritmirakenteita III

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

811120P Diskreetit rakenteet

815338A Ohjelmointikielten periaatteet Harjoitus 4 vastaukset

TIE PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli

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

Tutoriaaliläsnäoloista

Ehto- ja toistolauseet

Ohjelmoinnin perusteet Y Python

ITKP102 Ohjelmointi 1 (6 op)

4. Olio-ohjelmoinista lyhyesti 4.1

Kehittää ohjelmointitehtävien ratkaisemisessa tarvittavia metakognitioita!

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Java-kielen perusteet

811120P Diskreetit rakenteet

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

ELM GROUP 04. Teemu Laakso Henrik Talarmo

Ohjelmoinnin peruskurssi Y1

Alityypitys. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos

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

Tietotekniikan valintakoe

S BAB ABA A aas bba B bbs c

Attribuuttikieliopit

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

1.3 Lohkorakenne muodostetaan käyttämällä a) puolipistettä b) aaltosulkeita c) BEGIN ja END lausekkeita d) sisennystä

ITKP102 Ohjelmointi 1 (6 op)

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Staattinen tyyppijärjestelmä

Luokka Murtoluku uudelleen. Kirjoitetaan luokka Murtoluku uudelleen niin, että murtolukujen sieventäminen on mahdollista.

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssien laaja oppimäärä

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Transkriptio:

Tyyppipäättely Keijo Mattila kemattil@st.jyu.fi Anssi Pennanen anspenn@st.jyu.fi 1 Johdanto Luotettavuus ja tehokkuus ovat hyvän ohjelman ominaisuuksia. Pyrkimys näiden kahden ominaisuuden saavuttamiseen ovat vaikuttaneet suuresti tyyppipäättelyn kehittymiseen. Tyyppipäättelyllä saavutetaan muitakin ominaisuuksia ja niitä käsitellään kappaleessa 2. Usein, varsinkin olio-ohjelmoinnissa, tyyppipäättely yhdistetään implisiittisesti ja dynaamisesti tyypitettyihin kieliin kuten Smalltalk ja Self. Tyyppipäättelyä olioohjelmointikielissä käsitellään kappaleessa 3. Funktiokielten puolella tyyppipäättelyä on käytetty jo pitempään. Hindley/Milneralgoritmi on ehkä parhaiten tunnettu tyyppipäättelymenetelmä ja sitä käytetään ainakin staattisesti tyypitetyissä funktiokielissä ML ja Haskell. Funktiokielten osalta tyyppipäättelyä käsitellään kappaleessa 4. Lopuksi mainitsemme lyhyesti joitakin tapauksia, joissa käytetään tyyppipäättelyä. Olemme pyrkineet käyttämään seminaarityössä esiintyville termeille samoja suomennoksia ja määrittelyjä kuin Antti-Juhani Kaijanaho luentomonisteessaan [7]. 2 Tyyppipäättely Tiivistelmä Tässä seminaarityössä kerromme mitä tyyppipäättelyllä tarkoitetaan ja mitä etuja sillä voidaan saavuttaa. Käsittelemme päättelyä olio-ohjelmointikielissä ja funktiokielissä erikseen. Lopuksi mainitsemme tapauksia, joissa on käytetty tyyppipäättelyä. Ohjelmien sisältämä tyyppi-informaatio voi olla riittämätöntä. Tyyppi-informaatiota voidaan lisätä tyyppipäättelyn avulla. Tyyppipäättelyllä pyritään määräämään tai tarkentamaan ohjelman sisältämien vakioiden, operaattoreiden, muuttujien, lausekkeiden, parametrien ja paluuarvojen tyypit. Tyyppipäättely ei ole rajoittunut mihinkään tiettyyn ohjelmointikielten joukkoon. Yleisesti tyyppipäättely tapahtuu ennen ohjelman suoritusta, mutta näin ei ole aina. Urs Hölzle on toteuttanut Self-kääntäjän, joka perustuu tyyppipalautteeseen (type feedback) [6]. Tyyppipalautteen voidaan ajatella olevan dynaamista tyyppipäättelyä, koska siinä hyödynnetään ohjelman suorituksen aikana kerättyä tyyppitietämystä. 2.1 Edut Implisiittisesti tyypitetyillä (implicitly typed) ohjelmointikielillä toteutetut ohjelmat ovat pääasiallisia tyyppipäättelyn kohteita. Implisiittisesti tyypitetyssä ohjelmointikielessä ohjelmoijan ei tarvitse kirjoittaa tyyppimerkintöjä kaikille muuttujille, parametreille ja paluuarvoille. Joissakin ohjelmointikielissä ohjelmoija ei voi kirjoittaa tyyppimerkintöjä lainkaan. Ohjelmien ymmärrettävyyttä, luotettavuutta ja tehokkuutta voidaan parantaa tyyppipäättelyn avulla. Ymmärrettävyyden ja tehokkuuden lisääminen voivat olla tyyppipäättelyn osalta ristiriitaisia tavoitteita. Ymmärrettävyyden lisäämiseksi tarvitaan yleisiä ja tehokkuuden saavuttamiseksi mahdollisimman tarkkoja tyyppimäärittelyjä. 2.1.1 Ymmärrettävyys Ohjelmat, joiden lähdekoodit sisältävät vain vähän tai eivät lainkaan tyyppimerkintöjä, voivat olla vaikeasti luettavia ja ymmärrettäviä. Tyyppipäättelyn avulla ohjelmien lähdekoodiin voidaan lisätä automaattisesti tyyp- Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos. 1

pimerkintöjä ja näin parantaa ymmärrettävyyttä. Uudelleenkäyttö on usein tavoitteena. Tällöin lähdekoodiin automaattisesti lisättyjen tyyppimerkintöjen tulisi olla mahdollisimman yleisiä. Monimutkaiset ja vaikeaselkoiset tyyppimerkinnät eivät tietenkään lisää ymmärrettävyyttä. 2.1.2 Luotettavuus Ohjelmien tärkein ominaisuus on luotettavuus. Implisiittisesti tyypitetyillä kielillä toteutetuille ohjelmille ei voida suorittaa staattista tyyppitarkastusta (statical type checking), koska tyyppimäärittelyjä ei ole riittävästi. Puuttuvat määrittelyt voidaan selvittää tyyppipäättelyllä, jonka jälkeen ohjelma voidaan osoittaa tyyppivirheettömäksi. Päättely voi asettaa ohjelmaan dynaamisia tyyppitarkastuksia. Edellä mainittu staattinen tyyppitarkastus tehdään ennen ohjelman suoritusta. Tarkastuksen tehtävä on varmistaa, että ohjelma ei aiheuta tyyppivirheitä, eli ohjelma on tyyppivirheetön. Mikäli tarkastus ei voi suoraan osoittaa tyyppivirheettömyyttä, se usein joko estää ohjelman suorittamisen tai osoittaa tyyppivirheettömyyden epäsuorasti asettamalla dynaamisia tyyppitarkastuksia. Dynaaminen tyyppitarkastus (dynamical type checking) tehdään ohjelman suorituksen aikana. Tarkastuksen tehtävä on tutkia, aiheuttaako ohjelman osan suorittaminen tyyppivirheen. Mikäli aiheuttaa, tarkastus estää ohjelman osan suorittamisen ja aiheuttaa suoritusvirheen (run-time error). 2.1.3 Tehokkuus Hyvät kääntäjät kykenevät optimoimalla tuottamaan sitä tehokkaampia ohjelmia, mitä tarkempia ohjelman tyyppimääritykset ovat. Tehokkuutta lisätään esimerkiksi poistamalla käyttämätöntä koodia ja sijoittamalla metodin koodi sen kutsun tilalle (inlining of methods). Tyyppipäättely voi kasvattaa tehokkuutta myös vähentämällä dynaamisten tyyppitarkastusten tarvetta. 2.1.4 Kirjoittamisen helppous Tyyppipäättelyn eduiksi voidaan laskea myös se, ettei ohjelmoijan tarvitse kirjoittaa tyyppimerkintöjä, koska tyypit päätellään automaattisesti. Kun tyyppimerkintöjä ei tarvita, ohjelmointi on vaivattomampaa. Tämä korostuu varsinkin interaktiivisissa kielissä, jollainen esimerkiksi ML on. 3 Päättely olio-ohjelmointikielissä Esityksemme tyyppipäättelystä olioohjelmointikielissä perustuu kirjaan [10], jonka ovat kirjoittaneet Jens Palsberg ja Michael I. Schwartzbach. Opetuskäyttöön laadittu kirja käsittelee varsinaisesti olioohjelmointikielten tyyppijärjestelmiä. Kirjassa käytettävästä päättelymenetelmästä käytetään nimeä ehdolliset rajoitteet (conditional constraints). 3.1 BOPL-kieli Esimerkeissä käytämme BOPLohjelmointikieltä (Basic Object Programming Language). BOPL on implisiittisesti tyypitetty olio-ohjelmointikieli, joka on saanut vaikutteita seuraavista ohjelmointikielistä: Simula, Smalltalk, C++ ja Eiffel. BOPL-kielen kontekstivapaa kielioppi löytyy liitteestä A. Kielen ominaisuuksia ovat luokat ja oliot, sijoitus ja myöhäinen sidonta. BOPL-ohjelma koostuu äärellisestä joukosta luokkia ja niitä seuraavasta päälausekkeesta. Luokka määrittelee nimetyt attribuutit ja metodit. Metodiin liittyy nimettyjä parametreja ja toteutus, joka on lauseke. Olio on luokan ilmentymä. Kun olio luodaan, se varaa muistia attribuuteille. Jokainen olio sisältää informaatiota siitä, minkä luokan ilmentymä se on. BOPL-kielessä arvo tarkoittaa kokonaislukua, totuusarvoa, osoitinta olioon tai vakiota nil. Kieleen on määritelty olioiden luominen, arvojen sijoittaminen attribuutteihin, ehtolausekkeet, iterointi, peräkkäistys, selfmetamuuttuja, viestien lähetys olioille (eli 2 Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos.

metodin kutsuminen) ja mahdollisuus selvittää olion luokka ohjelman suorituksen aikana. self-c on luokan C selfmetamuuttujaan yhdistetty tyyppimuuttuja. 3.2 Tyyppijärjestelmä BOPL-tyyppijärjestelmässä tyypit ovat joukkoja, jotka muodostuvat luokista. Kaikkiin ohjelman muuttujiin, tarkoittaa attribuutteja ja parametreja, ja lausekkeisiin liitetään tyyppi. Ohjelman suorituksen aikana tyypit rajoittavat muuttujia ja lausekkeita: Muuttuja voi sisältää vain siihen liitetyn tyypin määrittelemiä arvoja. Lauseke voi saada vain siihen liitetyn tyypin määrittelemiä arvoja. 3.2.1 Tyyppi Määritellään tarkemmin mitä tyyppi tarkoittaa. Sananen (token) on joko Int, Bool tai luokan nimi. Tyyppi on sanasista muodostuva joukko. Esimerkkejä tyypeistä ovat: { }, {Int}, {Int, Bool, A, B, C}, {Circle, Triangle, Rectangle}. Tyypin määrittelemä arvo sisältää: Kokonaisluvut, jos Int kuuluu tyyppiin. Totuusarvot, jos Bool kuuluu tyyppiin. Osoittimet luokan olioihin, jos luokan nimi kuuluu tyyppiin. Tyypin määrittelemä arvo sisältää aina vakion nil, koska se on kaikkien muuttujien alkuarvo. 3.2.2 Tyyppimuuttuja Ohjelman muuttujiin, parametreihin ja lausekkeisiin liitetään tyyppi tyyppimuuttujan (type variable) avulla. Jokaiselle muuttujalle, parametrille ja lausekkeelle määrätään tyyppimuuttuja. Tyyppimuuttujan arvo on tyyppi. Tyyppimuuttujia merkitään seuraavasti: x on attribuuttiin x yhdistetty tyyppimuuttuja. f on parametriin f yhdistetty tyyppimuuttuja. E on lausekkeeseen E yhdistetty tyyppimuuttuja. 3.3 Tyyppirajoitteet Määritellään tyyppirajoitteet siten, että ne ovat kaikki muotoa: C X X Y c X Y Z alkurajoitteet etenevät rajoitteet ehdolliset rajoitteet Rajoitteissa X, Y ja Z ovat tyyppimuuttujia, c on sananen ja C on sanasjoukko. Alkurajoitteet muodostuvat esimerkiksi kokonaislukuja totuusarvovakioista ja niihin kohdistuvista operaatioista, kuten summa kokonaisluvuille ja konjuktio totuusarvoille. Etenevät rajoitteet muodostuvat esimerkiksi sijoituksista, ehtolausekkeista ja olioiden luomisesta. Ehdolliset rajoitteet muodostuvat metodien kutsuista. Tyyppirajoitteiden muodostamaa joukkoa kutsutaan rajoitejärjestelmäksi. Rajoitejärjestelmän ratkaiseminen tarkoittaa, että tyyppimuuttujille määritellään arvot. Voidaan osoittaa, että rajoitejärjestelmälle, joka koostuu edellä mainitun muotoisista rajoitteista, löytyy yksikäsitteinen ja samalla pienin mahdollinen ratkaisu. Rajoitteista on jätetty pois tyyppivirheettömyyden takaavat rajoitteet, jotka ovat muotoa: X C turvallisuusrajoitteet Turvallisuusrajoitteita ei tarvita rajoitejärjestelmän pienimmän mahdollisen ratkaisun laskemiseen. Ideana on ensin laskea tyyppipäättelyn avulla pienin mahdollinen ratkaisu rajoitejärjestelmälle ja sen jälkeen tarkistaa ratkaisu tyyppitarkastuksella. 3.3.1 Rajoitesäännöstö Tyyppimuuttujan arvo on tyyppi ja tyyppi on sanasista muodostuva joukko. Sanasia ovat int (kokonaislukutyyppi), bool (totuusarvotyyppi) ja luokan nimet (luokkatyypit). Kun X ja Y ovat tyyppimuuttujia, merkinnällä X Y tarkoitetaan, että kaikki muuttujan X tyypin sisältämät sanaset löytyvät myös muuttujan Y tyypistä (sanasjoukosta). Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos. 3

Huomautetaan vielä, että säännöistä on poistettu turvallisuusrajoitteet. Esimerkiksi summasta EXP 1 + EXP 2 edellytetään vain, että summa saa arvokseen ainakin kokonaislukuja. Tyyppitarkastuksen käyttämät turvallisuusrajoitteet edellyttäisivät, että summa sekä lausekkeet EXP 1 ja EXP 2 saavat arvoikseen ainoastaan kokonaislukuja. Tyyppipäättelyssä tarvittavia rajoitteita muodostetaan seuraavien sääntöjen perusteella: 1. "INT". Kaikille kokonaislukuvakioille k, esimerkiksi 27, määritellään rajoitteet: k {Int} 2. "true"ja "false". Totuusarvoisille vakioille määritellään true {Bool} ja false {Bool} 3. "nil". Ei tarvitse rajoituksia. 4. "EXP 1 + EXP 2 ". Summalle määritellään EXP 1 + EXP 2 {Int} Tämä tarkoittaa, että summan arvot ovat ainakin kokonaislukuja. 5. "EXP 1 - EXP 2 ". Erotukselle määritellään EXP 1 EXP 2 {Int} Tämä tarkoittaa, että erotuksen arvot ovat ainakin kokonaislukuja. 6. "EXP 1 * EXP 2 ". Tulolle määritellään EXP 1 EXP 2 {Int} Tämä tarkoittaa, että tulon arvot ovat ainakin kokonaislukuja. 7. "EXP 1 = EXP 2 ". Yhtäsuuruuden vertailulle määritellään EXP 1 = EXP 2 {Bool} Tämä tarkoittaa, että vertailun tulokset ovat ainakin totuusarvoja. 8. "EXP 1 and EXP 2 ". Konjuktiolle määritellään EXP 1 and EXP 2 {Bool} Tämä tarkoittaa, että konjuktio saa ainakin totuusarvoja. 9. "EXP 1 or EXP 2 ". Disjunktiolle määritellään EXP 1 or EXP 2 {Bool} Tämä tarkoittaa, että disjunktio saa ainakin totuusarvoja. 10. "EXP 1 < EXP 2 ". Epäyhtälölle määritellään EXP 1 < EXP 2 {Bool} Tämä tarkoittaa, että vertailun tulokset ovat ainakin totuusarvoja. 11. "EXP not". Negaatiolle määritellään EXP not {Bool} Tämä tarkoittaa, että negaation tulokset ovat ainakin totuusarvoja. 12. "ID := EXP". Sijoitukselle määritellään rajoitteet: EXP ID ID := EXP EXP Tämä tarkoittaa, että lausekkeen EXP saamat arvot ovat sellaisia, että myös muuttuja ID voi sisältää niitä. Lisäksi sijoituslausekkeen tuottamat arvot sisältävät ainakin lausekkeen EXP saamat arvot. 13. "EXP 1 ; EXP 2 ". Peräkkäistykselle määritellään EXP 1 ; EXP 2 EXP 2 Tämä tarkoittaa, että peräkkäistyksen tuottama arvo sisältää ainakin lausekkeen EXP 2 saamat arvot. 14. "if EXP 1 then EXP 2 else EXP 3 ". Ehtolauseelle määritellään rajoitteet: EXP 1 {Bool} if EXP 1 then EXP 2 else EXP 3 EXP 2 if EXP 1 then EXP 2 else EXP 3 EXP 3 Tämä tarkoittaa, että lauseke EXP 1 saa ainakin totuusarvoja. Kaksi muuta rajoitetta tarkoittavat, että ehtolausekkeen tuottama arvo sisältää ainakin lausekkeiden EXP 2 ja EXP 3 saamat arvot. 4 Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos.

15. "while EXP 1 do EXP 2 ". Iteroinnille määritellään EXP 1 {Bool} Tämä tarkoittaa, että lauseke EXP 1 saa ainakin totuusarvoja. 16. "self". Metamuuttujalle self määritellään self C {C} Tämä tarkoittaa, että selfmetamuuttuja saa arvokseen ainakin osoittimen luokan C olioon. C on luokka, jonka metodissa self esiintyy. 17. "ID new". Olion luomiselle määritellään ID new {ID} Tämä tarkoittaa, että lauseke ID new saa arvokseen ainakin osoittimia luokan ID olioihin. 18. "EXP class new". Epäsuoralle olion luomiselle määritellään EXP class new EXP Tämä tarkoittaa, että epäsuora olion luonti tuottaa ainakin osoittimia olioihin, jotka ovat saman luokan ilmentymiä, kuin ne oliot, joihin lausekkeen EXP tuottamat osoittimet osoittavat. 19. "EXP instance of { IDLIST }". Dynaamiselle tarkastukselle luokkaan kuulumiselle määritellään EXP instance of {IDLIST} {IDLIST} Tämä tarkoittaa, että lauseke saa arvokseen osoittimia ainakin listassa IDLIST mainittujen luokkien olioihin. 20. "ID". Muuttujalle tai parametrille, jolle on asetettu tyyppi, määritellään ID the declared type Tämä tarkoittaa, että muuttuja ID saa ainakin sellaisia arvoja, joita sen tyyppi määrittelee. 21. Viestin lähetykselle määritellään ehdolliset rajoitteet: C EXP EXP 1 ID 1... EXP n ID n EXP.ID(EXP 1,...,EXP n ) EXP 0 Ensimmäiset n rajoitetta tarkoittavat, että metodin parametrit saavat ainakin sellaisia arvoja, joita metodin kutsun argumentit saavat. Viimeinen rajoite tarkoittaa, että metodin kutsu tuottaa ainakin semmoisia arvoja, joita metodin viimeinen lauseke EXP 0 tuottaa. 3.4 Päättelyalgoritmi Tyyppipäättelymenetelmä, jota Palsberg ja Schwartzbach esittelevät sekä artikkelissaan [9] että kirjassaan [10], sisältää kolme päävaihetta: 1. Rakennetaan ohjelmasta rajoiteverkko (trace graph). 2. Muodostetaan tyyppirajoitteet rajoiteverkon avulla. 3. Lasketaan pienin mahdollinen ratkaisu tyyppirajoitteille kiintopistemenetelmällä (fixed-point computation). Rajoiteverkko muodostuu solmuista ja niitä yhdistävistä kaarista. Solmut kuvaavat ohjelman päälauseketta ja luokkien metodeja. Solmut sisältävät paikallisia tyyppirajoitteita, jotka muodostuvat metodin toteutuksesta. Kaaret edustavat metodien kutsuja. Kun päälausekkeesta tai metodin toteutuksesta kutsutaan esimerkiksi metodia m, muodostetaan solmu jokaiselle metodille m, jonka parametrien määrä vastaa argumenttien määrää. Jos metodin m toteuttavia luokkia on N kappaletta, myös uusia solmuja muodostuu N kappaletta ja jokainen yhdistetään kaarella kutsuvaan solmuun. Kaaret sisältävät metodin kutsuun liittyviä ehdollisia rajoitteita, katso päättelysääntö 21. Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos. 5

Globaalit rajoitteet muodostetaan yhdistämällä solmujen ja kaarien sisältämät rajoitteet. Globaalit rajoitteet ovat toisessa päävaiheessa mainittuja tyyppirajoitteita. Jos globaaleilla tyyppirajoitteilla on ratkaisu, ohjelma on tyypitettävissä. Ratkaisun tuloksena saadaan tyypitetty ohjelma, joka voidaan edelleen tyyppitarkastaa ja näin varmistaa tyyppivirheettömyys. 3.5 Esimerkki Esittelemme esimerkkiohjelman avulla, miten tyyppipäättelyä periaatteessa käytetään. Ohjelman ei ole tarkoitus tehdä mitään hyödyllistä: Class A method f(a) a + 5 Class B var c method f(b) c := false; b or c (A new).f(2)+3 Esimerkkiohjelmasta muodostuu seuraavanlaiset rajoitteet: 1) 5 {Int} 4) a + 5 {Int} 2) false {Bool} 12) c false 12) c := false false 9) b or c {Bool} 13) c := false; b or c b or c 17) A new {A} 1) 2 {Int} 21) A A new { 2 a (A new).f(2) a + 5 21) B A new 2 b (A new).f(2) c := false; b or c 1) 3 {Int} 4) (A new).f(2)+3 {Int} Rajoitteiden kohdalle merkityt numerot tarkoittavat käytetyn rajoitesäännön numeroa. Yllä esitetyn rajoitejärjestelmän pienin mahdollinen ratkaisu on: 5 = {Int} a+5 = {Int} false = {Bool} c = {Bool} c := false = {Bool} b or c = {Bool} c := false; b or c = {Bool} A new = {A} a = {Int} b = { } (A new).f(2) = {Int} 3 = {Int} (A new).f(2)+3 = {Int} Esitellään vielä esimerkkiohjelma, joka on selvästi tyyppivirheellinen: Class C method n(i) i + 1 (C new).n(true) Ohjelmasta muodostuu seuraavanlaiset rajoitteet: 1) 1 {Int} 4) i + 1 {Int} 17) C new {C} 2) true {Bool} 21) C C new { true i (C new).n(true) i + 1 Yllä olevan rajoitejärjestelmän pienin mahdollinen ratkaisu on: i = {Bool} 1 = {Int} i+1 = {Int} C new = {C} true = {Bool} (C new).n(true) = {Int} 6 Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos.

Nyt on päätelty, että parametri i on tyyppiä Bool. Tämä on mahdollista, koska tyyppisäännöstö ei sisällä turvallisuusrajoitteita. Ohjelman suorittaminen aiheuttaisi suoritusvirheen. Ohjelma ja edellä päätellyt tyypit voidaan kuitenkin syöttää tyyppitarkastajalle, joka huomaisi yhteenlaskun aiheuttaman tyyppivirheen. 4 Päättely funktiokielissä Parhaiten tunnettu algoritmi tyyppipäättelyyn lienee Hindley/Milner-algoritmi, jota käytetään ainakin ML-kääntäjissä. ML on staattisesti tyypitetty (statically typed) funktiokieli ja se käyttää tyyppipäättelyä tyyppivirheettömyyden selvittämiseen. Staattisesti tyypitetty ohjelmointikieli vaatii toteutukseltaan, että tyyppivirheet havaitaan ennen ohjelman suoritusta. Toteutuksen tulee huomata kaikki ohjelmointikielen määrittelemät tyyppivirheet. Tämä edellyttää aina staattista tyyppitarkastusta. Hindley/Milner-algoritmin kehittäjinä ovat toimineet J. Roger Hindley ja Robin Milner, joista Milner yhdisti algoritmin tyyppipäättelyyn [8]. Hindley/Milner-algoritmi hyödyntää samastusalgoritmia unification. Seuraavissa kappaleissa esittelemme periaattellisella tasolla, miten tyyppipäättely suoritetaan funktiokielissä. Esityksemme perustuu Luca Cardellin artikkeliin [3], koska siinä Hindley/Milner-algoritmin käyttö tyyppipäättelyssä on esitetty helppotajuisesti. 4.1 Esimerkkikieli Tyyppipäättelyn esimerkeissä käytetään yksinkertaista tyypitettyä λ-laskentaa (typed λ- calculus), joka sisältää vakioita ja jota Cardelli pitää ML-kielen ytimenä. Konkreettinen syntaksi on: Exp ::= Ide if Exp then Exp else Exp fun( Ide ) Exp Exp ( Exp ) let Decl in Exp ( Exp ) Decl ::= Ide = Exp Decl then Decl rec Decl ( Decl ) Syntaksissa Ide tarkoittaa tunnisteita, Exp lausekkeita, Decl määrittelyitä ja fun on sama kuin λ. Seuraava esimerkki syntaksista on ohjelma, joka määrittelee kertomafunktion ja laskee sillä nollan kertoman. Esimerkissä oletetaan, että kokonaislukuvakiot ja funktiot zero, succ, times ja pred ovat käytettävissä. let rec factorial = fun(n) if zero(n) then succ(0) else times(n)factorial(pred(n)) in factorial(0) 4.2 Tyypit Tyyppi voi olla tyyppimuuttuja, joka edustaa mielivaltaista tyyppiä. Tyyppimuuttujaa merkitään kreikkalaisilla kirjaimilla α, β, jne.. Tyyppi voi olla myös tyyppioperaattori. Operaattorit kuten int ja bool ovat parametrittomia tyyppioperaattoreita. Parametriset tyyppioperaattorit kuten (funktiotyyppi) ja (karteesisen tulon tyyppi) ottavat yhden tai useamman tyypin argumenttina. Kaikkein yleistetyimmät muodot mainituista operaattoreista ovat α β (mikä tahansa funktio) ja α β (mikä tahansa pari). α ja β voidaan korvata millä tahansa tyypillä rajoitetumman funktion tai parin muodostamiseksi. Määritellään funktion ja karteesisen tulon lisäksi uusi parametrinen tyyppioperaattori lista, jota merkitään list. Järjestelmän tyyppiympäristö voisi koostua aluksi seuraavista Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos. 7

vakioista ja perusoperaatioista: true, false : bool 0, 1,... : int succ, pred : int int zero : int bool pair : α (β (α β)) fst : (α β) α snd : (α β) β nil : α list cons : (α α list) α list hd : α list α tl : α list α list null : α list bool Tyyppi α list on homogeenisen listan tyyppi. Homogeenisessa listassa kaikkien elementtien tyyppi on α. 4.3 Samastusalgoritmi Tyyppitarkastus, ja siihen liittyvä tyyppipäättely, tarkoittaa nyt tyyppioperaattoreiden sovittamista ja tyyppimuuttujien tarkentamista. Aina, kun jotakin tyyppimuuttujaa tarkennetaan, täytyy kaikkia kyseisen tyyppimuuttujan esiintymiä tarkentaa samalla tavalla. Funktiotyypin α α tapauksessa sallittuja tarkennuksia ovat esimerkiksi int int, bool bool ja (β γ) (β γ). Esimerkiksi tarkennus int bool on laiton. Ympäristöstä riippuva tarkennusprosessi suoritetaan samastusalgoritmilla. Samastusalgoritmi epäonnistuu, kun yritetään sovittaa kahta erilaista tyyppioperaattoria, kuten int ja bool. Algoritmi epäonnistuu myös silloin, kun yritetään tarkentaa tyyppimuuttujaa sellaisella tyypillä, joka sisältää kyseisen tyyppimuuttujan, esimerkiksi α tarkennetaan tyypiksi α β. Tämä on kielletty, koska se aiheuttaa kehärakenteen. Yksinkertaiseen esimerkkiin tyyppipäättelystä käytetään identiteettifunktiota Id = fun(x)x, jolla on tyyppi α α, koska se kuvaa jokaisen tyypin samaksi tyypiksi. Lausekkeessa Id(0) nollan tyyppi, esimerkiksi int, sovitetaan funktion Id määrittelyalueeksi, jolloin tässä yhteydessä funktiolle Id saadaan tarkennettu tyyppi int int. Tällöin lausekkeen Id(0) tyyppi on funktion Id arvojoukko, eli int. Yleisemmin tyyppipäättely tapahtuu kielen rakenteeseen perustuvien tyyppipäättelysääntöjen ja perusoperaatioille määriteltyjen tyyppien avulla. 4.3.1 Algoritmin kuvaus Tyyppitarkastuksen ja -päättelyn suorittavaa perusalgoritmia voidaan kuvailla niiden tapausten avulla, joita algoritmi soveltaa tilanteesta riippuen: Tapaus 1: Kun uusi muuttuja x esiintyy siten, että fun sitoo sitä, muuttujalle määritellään uusi tyyppimuuttuja α. Tämä tarkoittaa, että muuttujan tyyppi täytyy edelleen päätellä sen esiintymisympäristön perusteella. Uusi pari <x,α> talletetaan tyyppiympäristöön, josta paria etsitään aina kun löydetään x:n esiintymä ja tällöin esiintymän tyypiksi saadaan α. Tapaus 2: Ehtolausekkeessa if komponentille sovitetaan tyyppi bool. Haarojen then ja else tyypit samaistetaan, jotta koko ehtolausekkeelle voidaan päätellä yksilöllinen tyyppi. Tapaus 3: Abstraktiossa fun(x)e lausekkeen e tyyppi päätellään ympäristössä, jossa muuttujalle x on määritelty uusi tyyppimuuttuja. Tapaus 4: Funktiokutsussa f(a) f:n tyyppi samaistetaan tyyppiin A β, missä A on a:n tyyppi ja β on uusi tyyppimuuttuja. Tämä edellyttää, että f:n tyyppi on funktiotyyppi ja jonka määrittelyalue voidaan samaistaa tyyppiin A. Funktiokutsun tyypiksi palautuu β tai sen jokin tarkennus. Algoritmin kuvauksesta puuttuu tapauksia, joita sovelletaan yksityiskohtaisemmissa tilanteissa. Puuttuvia tapauksia ei käsitellä. 4.4 Funktion tyyppipäättely Varsinainen esimerkki tyyppipäättelystä suoritetaan funktiolla length, joka laskee listan alkioiden lukumäärän: let rec length = fun(l) if null(l) then 0 8 Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos.

else succ(length(tl(l))) in... Funktion length tyyppi on α list int. Tyyppi on monimuotoinen (polymorphic), koska funktio toimii kaikille homogeenisille listoille. Menetelmä, jolla funktion tyyppi päätellään, voidaan kuvata periaattellisella tasolla seuraavasti: rakennetaan tyyppirajoitteista koostuva järjestelmä, josta ratkaistaan tarvittavat tyyppimuuttujat. Käytännössä tyyppitarkastus ja tyyppipäättely suoritetaan päättelemällä ohjelman alimman tason lausekkeet ja etenemällä niistä kohti ohjelman päälauseketta sovittamalla ja tarkentamalla tyyppejä (bottom-up). Yksittäisen lausekkeen tyyppi päätellään lausekkeen sisältämien alilausekkeiden, tyyppiympäristön asettamien tyyppirajoitteiden ja perustyypeille määriteltyjen tyyppien perusteella. 4.4.1 Esimerkki Suoritetaan funktion length tyyppipäättely periatteellisella tasolla. Funktiolle rakennetaan kuvan 1 mukainen tyyppirajoitteista koostuva järjestelmä. Kohdat [1-4] ilmaisevat perusoperaatioille määriteltyjä tyyppirajoitteita, jotka esiteltiin aikaisemmin ja jotka siis tunnetaan etukäteen. Ehtolauseke edellyttää [5-8], että testin tuloksen täytyy olla totuusarvo ja molemmilla ehdon vaihtoehdoilla täytyy olla sama tyyppi γ, joka on myös koko ehtolausekkeen tyyppi. Esimerkkiohjelmassa käytettävät neljä funktiota null, tl, succ ja length määrittelevät rajoitteet [9-20]. Niissä ilmaistaan, että jokaisella funktiolla tulee olla funktiotyyppi, funktion argumentin tulee vastata funktion määrittelyaluetta ja funktion tuloksen täytyy vastata funktion arvoaluetta. Lausekkeella fun [23] on tyyppi µ ν, kun parametrilla on tyyppi µ ja toteutuksella on tyyppi ν. Tyyppitarkastus ja tyyppipäättely funktiolle length vaatii (i) rakennetun tyyppirajoitejärjestelmän toteamista johdonmukaiseksi (järjestelmä ei saa esimerkiksi edellyttää, että int = bool) ja (ii) rajoitteiden ratkaisemista siten, että tyyppimuuttuja π saadaan tarkennettua. Funktion length odotettavissa oleva tyyppi [1] null : αlist bool [2] tl : βlist βlist [3] 0 : int [4] succ : int int [5] null(l) : bool [6] 0 : γ [7] succ(length(tl(l))) : γ [8] if null(l) then 0 else succ(length(tl(l))) : γ [9] null : δ ε [10] l : δ [11] null(l) : ε [12] tl : φ χ [13] l : φ [14] tl(l) : χ [15] length : η ι [16] tl(l) : η [17] length(tl(l)) : ι [18] succ : κ λ [19] length(tl(l)) : κ [20] succ(length(tl(l))) : λ [21] l : µ [22] if null(l) then 0 else succ(length(tl(l))) : ν [23] fun(l) if null(l) then 0 else succ(length(tl(l))) : µ ν [24] length : π [25] fun(l) if null(l) then 0 else succ(length(tl(l))) : π Kuva 1: Funktion length tyyppirajoitteet π = α list int voidaan päätellä seuraavasti: π = µ ν kohdat [25, 23] µ = φ = β list kohdat [21, 13, 12, 2] ν = γ = int kohdat [22, 8, 6, 3] Huomattavasti lisää työtä tarvittaisiin koko rajoitejärjestelmän osoittamiseksi johdonmukaiseksi. Se, että tyyppimuuuttuja β voi edustaa mielivaltaista tyyppiä, vaatii myös lisää työtä. Kumpaakaan ominaisuutta ei tässä osoiteta. 5 Yhteenveto Tässä seminaarityössä esittelimme tyyppipäättelyä erikseen olio-ohjelmointi- ja funktiokielille. Molempien osalta tyyppipäättelyä Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos. 9

käsiteltiin periaattellisella tasolla. Konkreettisia algoritmeja tai toteutuksia ei esitelty. Olio-ohjelmointikielille esitellyn päättelymenetelmän merkitys on lähinnä opetuksellinen. Ole Agesenin väitöskirjassaan [1] suorittamat mittaukset osoittavat, että menetelmän resurssivaatimukset ovat kohtuuttomia todellisten ohjelmien päättelemiseen. Funktiokielille esitelty Hindley/Milner-algoritmi on sen sijaan todettu riittävän tehokkaaksi ja käyttökelpoiseksi todellisissa ohjelmissa. Lopuksi haluamme mainita joitakin tyyppipäättelyn käyttökohteita. Tyyppipäättelyä käytetään SmallEiffelkääntäjässä, kun Eiffel-kielinen lähdekoodi käännetään C-kielelle [4]. Herman Pande ja Barbara Ryder esittelevät tyyppipäättelyä C++-kielessä [11]. Fritz Henglein ja Jakob Rehof käyttävät tyyppipäättelyä Schemeohjelmointikielellä tehtyjen ohjelmien muuntamiseen ML-kielisiksi ohjelmiksi [5]. Scheme on dynaamisesti tyypitetty funktiokieli. Tyyppipäättelyjärjestelmän FL-kielelle ovat esitelleet Alexander Aiken ja Brian Murphy [2]. Olio-ohjelmointikielistä Smalltalk:lle tyyppipäättelyä on esitellyt Norihisa Suzuki [12] ja Self:lle Ole Agesen [1]. Viitteet [1] Ole Agesen: Concerete Type Inference: Delivering Object-Oriented Applications, väitöskirja, Standford University, Department of Computer Science, 1996, Published by Sun Microsystem Laboratories(SMLI TR-96-52). [2] Alexander Aiken ja Brian R. Murphy: Static Type Inference in a Dynamically Typed Language, In Eighteenth Annual ACM Symposium on Principles of Programming Languages, tammikuu 1991. [3] Luca Cardelli: Basic Polymorphic Typechecking, Science of Computer Programming, 8(2), huhtikuu 1987. [4] Suzanne Collin, Dominique Colnet ja Olivier Zra: Type Inference for Late Binding. The SmallEiffel Compiler, In Joint Modular Languages Conference (JMLC 97), Volume 1204 of Lecture Notes in Computer Sciences, ss. 67 81, 1997, Springer-Verlag. [5] Fritz Henglein ja Jakob Rehof: Safe polymorphic type inference for a Dynamically Typed Language: Translating Scheme to ML, In Proc. Conf. on Functional Programming Languages and Computer Architecture (FPCA), ACM Press, lokakuu 1995, La Jolla, CA USA. [6] Urs Hölzle: Adaptive Optimization in Self: Reconciling High Performance with Exploratory Programming, väitöskirja, Standford University, Department of Computer Science, 1995, Published by Sun Microsystem Laboratories(SMLI TR-95-35). [7] Antti-Juhani Kaijanaho: Ohjelmointikielten periaatteet, Luentomoniste, Jyväskylän yliopisto, tietotekniikan laitos, 2002. [8] Robin Milner: A Theory of Type Polymorphism in Programming, Journal of Computer and System Sciences, 17(3), huhtikuu 1978. [9] Jens Palsberg ja Michael I. Schwartzbach: Object-Oriented Type Inference, In OOPSLA 91, ACM SIGPLAN Sixth Annual Conference on Object-Oriented Programming Systems, Languages and Applications, lokakuu 1991, Phoenix, Arizona. [10] Jens Palsberg ja Michael I. Schwartzbach: Object-Oriented Type Systems, Chichester, England: John Wiley & Sons, 1993. [11] Hemant D. Pande ja Barbara G. Ryder: Static Type Determination and Aliasing for C++, Technical Report LCSR-TR-236, Department of Computer Science, Rutgers University, lokakuu 1995. [12] Norihisa Suzuki: Inferring Types in Smalltalk, In Conference Record of the Eighth Annual ACM Symposium on Principles of Programming Languages, tammikuu 1981, Williamsburg, Virginia. 10 Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos.

Liite A BOPL-kielen kontekstiton kielioppi: PROGRAM CLASSLIST? CLASSLIST CLASS ::= CLASSLIST? EXP ::= ɛ CLASSLIST ::= CLASS CLASSLIST CLASS ::= class ID VARLIST? METHODLIST? class ID is ID METHODLIST? ::= ɛ METHODLIST METHODLIST ::= METHOD METHODLIST METHOD METHOD ::= method ID FORMALS EXP VARLIST? VARLIST VAR ::= ɛ VARLIST ::= VAR VARLIST VAR ::= var DEC FORMALS ::= ( DECLIST? ) DECLIST? ::= ɛ DECLIST DECLIST ::= DEC DECLIST ; DEC DEC ::= IDLIST IDLIST ::= ID IDLIST, ID EXP ::= INT EXP BINOP EXP false true EXP not ID := EXP EXP ; EXP if EXP then EXP else EXP while EXP do EXP nil self ID new EXP class new EXP instance-of { IDLIST } ID EXP. ID ( EXPLIST? ) ( EXP ) BINOP ::= + - * = and or < EXPLIST? ::= ɛ EXPLIST EXPLIST ::= EXP EXPLIST, EXP INT ::= DIGIT DIGIT INT DIGIT ::= 0 1 2 3 4 5 6 7 8 9 ID ::= LETTER ID LETTER ID DIGIT LETTER ::= a b... z A B... Z Ohjelmointikielten periaatteet, päätösseminaari 2.12.2002, Jyväskylän yliopisto, tietotekniikan laitos. 11