TIES542 kevät 2009 Rekursiiviset tyypit

Samankaltaiset tiedostot
14.1 Rekursio tyypitetyssä lambda-kielessä

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

TIES542 kevät 2009 Tyyppijärjestelmän laajennoksia

Rekursiiviset tyypit

Luku 15. Parametripolymorfismi Kumitus

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

Rekursiiviset tyypit - teoria

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 26. kesäkuuta 2013

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

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

Ydin-Haskell Tiivismoniste

Yksinkertaiset tyypit

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

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

TIES542 kevät 2009 Tyyppiteorian alkeet

Haskell ohjelmointikielen tyyppijärjestelmä

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

Taas laskin. TIES341 Funktio ohjelmointi 2 Kevät 2006

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Staattinen tyyppijärjestelmä

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

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 16. toukokuuta 2011

jäsennyksestä TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho 29. syyskuuta 2016 TIETOTEKNIIKAN LAITOS Kontekstittomien kielioppien

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

jäsentäminen TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 26. marraskuuta 2015 TIETOTEKNIIKAN LAITOS

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

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

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 10. kesäkuuta 2013

formalismeja TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 15. joulukuuta 2015 TIETOTEKNIIKAN LAITOS

TIEA341 Funktio-ohjelmointi 1, kevät 2008

semantiikasta TIE448 Kääntäjätekniikka, syksy 2009 Antti-Juhani Kaijanaho 5. lokakuuta 2009 TIETOTEKNIIKAN LAITOS Ohjelmointikielten staattisesta

3. Muuttujat ja operaatiot 3.1

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

jäsentämisestä TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 27. marraskuuta 2015 TIETOTEKNIIKAN LAITOS

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit.

Kesälukio 2000 PK2 Tauluharjoituksia I Mallivastaukset

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

Attribuuttikieliopit

Täydentäviä muistiinpanoja jäsennysalgoritmeista

Lisää pysähtymisaiheisia ongelmia

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 29. toukokuuta 2013

Vasen johto S AB ab ab esittää jäsennyspuun kasvattamista vasemmalta alkaen:

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

Rajoittamattomat kieliopit (Unrestricted Grammars)

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

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

Ohjelmoinnin peruskurssien laaja oppimäärä

TIES542 kevät 2009 Aliohjelmien formalisointia lambda-kieli

S BAB ABA A aas bba B bbs c

Harjoitus 5 -- Ratkaisut

Ohjelmointikielten periaatteet. Antti-Juhani Kaijanaho

Pinoautomaatit. TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 6. kesäkuuta 2013 TIETOTEKNIIKAN LAITOS. Pinoautomaatit.

Ruby. Tampere University of Technology Department of Pervasive Computing TIE Principles of Programming Languages

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

MS-A0204 Differentiaali- ja integraalilaskenta 2 (ELEC2) Luento 7: Pienimmän neliösumman menetelmä ja Newtonin menetelmä.

811120P Diskreetit rakenteet

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 26. tammikuuta 2012

Luku 3. Listankäsittelyä. 3.1 Listat

Ohjelmoinnin peruskurssien laaja oppimäärä

Täydentäviä muistiinpanoja Turingin koneiden vaihtoehdoista

Johdatus diskreettiin matematiikkaan Harjoitus 5, Ratkaise rekursioyhtälö

5.5 Jäsenninkombinaattoreista

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

Chomskyn hierarkia ja yhteysherkät kieliopit

TIES542 kevät 2009 Oliokielten erityiskysymyksiä

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 31. maaliskuuta 2011

T Rinnakkaiset ja hajautetut digitaaliset järjestelmät Prosessialgebra

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

TIES542 kevät 2009 Denotaatio

Tietorakenteet ja algoritmit - syksy

ELM GROUP 04. Teemu Laakso Henrik Talarmo

Dart. Ryhmä 38. Ville Tahvanainen. Juha Häkli

5/20: Algoritmirakenteita III

Tutoriaaliläsnäoloista

Ohjelmoinnin peruskurssien laaja oppimäärä

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 12. kesäkuuta 2013

TIES542 kevät 2009 Yhteismuistisamanaikaisuus

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 31. maaliskuuta 2011

Tietorakenteet ja algoritmit

Täydentäviä muistiinpanoja kontekstittomien kielioppien jäsentämisestä

Kompleksilukujen kunnan konstruointi

Tyyppipäättely. Keijo Mattila Tiivistelmä

7/20: Paketti kasassa ensimmäistä kertaa

Konvergenssilauseita

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 16. helmikuuta 2012

Gaussin ja Jordanin eliminointimenetelmä

Chomskyn hierarkia. tyyppi 0 on juuri esitelty (ja esitellään kohta lisää) tyypit 2 ja 3 kurssilla Ohjelmoinnin ja laskennan perusmallit

2.2.1 Ratkaiseminen arvausta sovittamalla

Epäyhtälöt ovat yksi matemaatikon voimakkaimmista

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

vaihtoehtoja TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho 13. lokakuuta 2016 TIETOTEKNIIKAN LAITOS

T Syksy 2004 Logiikka tietotekniikassa: perusteet Laskuharjoitus 7 (opetusmoniste, kappaleet )

Ohjelmoinnin peruskurssien laaja oppimäärä

Nopea kertolasku, Karatsuban algoritmi

Luku 2. Ohjelmointi laskentana. 2.1 Laskento

1. Olio-ohjelmointi 1.1

3.2 Matriisien laskutoimitukset. 3.2 Matriisien laskutoimitukset. 3.2 Matriisien laskutoimitukset. 3.2 Matriisien laskutoimitukset

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Transkriptio:

TIES542 kevät 2009 Rekursiiviset tyypit Antti-Juhani Kaijanaho 17. helmikuuta 2009 Edellisessä monisteessa esitetyt tietue- ja varianttityypit eivät yksinään riitä kovin mielenkiintoisten tietorakenteiden toteuttamiseen. Useimmissa ohjelmissa tarvitaan erilaisia puurakenteita, jotka on luonnollisinta esittää suoraan käyttämällä itseviittaavia tyyppejä. Esimerkiksi yksöislinkitetty kokonaislukulista voidaan määritellä C-kielessä seuraavasti: struct intlist { int datum; struct intlist next; }; Tyypitetyn (ja rakenteisilla tyypeillä laajennetun) lambda-kielen syntaksilla se voitaisiin kirjoittaa esimerkiksi näin: NumList = null : (), notnull : { datum : Num, next : NumList }... paitsi että tuohon kieleen ei kuulu minkäänlaista tapaa antaa nimiä (saati rekursiivisia määritelmiä!) tyypeille. Tässä monisteessa käsitellään rekursiivisten tyyppimääritelmien problematiikkaa ja sen ratkaisumalleja. Monisteen päälähde on Pierce (2002, luvut 21 ja 22). 1 Rekursiiviset termit tyypitetyssä lambda-kielessä Rekursiivisten tyyppien käsittelyssä tarvitaan rekursiivisia termejä. Koska yksinkertaisesti tyypitetyssä lambda-kielessä ei ole valmista kiintopisteoperaattoria, on sellainen lisättävä: 1

t, u ::= µx : T t Γ, x : T t : T Γ µx : T t : T µx : T t t[x := µx : T t] 2 Rekursiiviset tyypit lambda-kielessä Tyyppiteorian tapa lisätä rekursiiviset tyypit lambda-kieleen on ottaa käyttöön tyyppioperaattori µ, joka mahdollistaa rekursion: α TyVars T, U ::= α µα T Esimerkiksi lukulistan tyyppi voitaisiin kirjoittaa µα null : (), notnull : (Num, α) ; operaattorin µα vaikutusalueella tyyppimuuttuja α edustaa koko pitkää µ:lla alkavaa tyyppilauseketta. Esimerkki 1. NumList = µα null : (), notnull : (Num, α) sum = µf : NumList Num λl : NumList case l of null = x 0 notnull = x let (a, r) = x in a + fr Esimerkissä nimet ovat vain lyhennysmerkintöjä; rekursiota ei niiden kautta sallita. Tämän laajennuksen tyypitys- ja laskentasäännöt vaativat jonkin verran pohdintaa. Olisi nimittäin kiva, jos tyyppien yhtäläisyys noudattaisi kiintopisteperiaatetta µα T (α) = T (µα T (α)) missä T (α) on jokin tyyppilauseke, jossa α esiintyy. Lukulistan tapauksessa sama kiintopisteyhtälö saa muodon µα null : (), notnull : (Num, α) = null : (), notnull : (Num, µα null : (), notnull : (Num, α) ) 2

Rekursiivisten tyyppien ongelma on, että on varsin hankalaa määritellä algoritmi, joka toteuttaa sellaisen tyyppien yhtäläisyysvertailun, jolle tuo yhtälö pätee. Algoritmi, joka toteuttaa tämän yhtälön sellaisenaan, toteuttaa ekvirekursion (engl. equirecursion). 3 Nimiyhtäläisyys Yksinkertaisin ja ohjelmointikielissä yleisin tapa ratkaista rekursiivisuuden ongelma on vaatia, että jokainen tyyppi nimetään, ja sallia tällaisten nimettyjen määritelmien keskinäinen rekursio (yleensä siten rajoitettuna, että rekursion tulee kulkea osoittimen kautta, mikäli kielessä on erillinen osoitintyyppi). Lisäksi tässä ratkaisussa julistetaan, että kaksi eri nimistä tyyppiä ovat eri tyyppejä vaikka niillä olisikin sama rakenne. Tätä ratkaisutapaa sanotaan nimiyhtäläisyydeksi (engl. name equivalence). 4 Isorekursio Hieman monimutkaisempi ja edellä annettuun lambda-kielen laajennokseen sopiva mutta kuitenkin täyttä ekvirekursiota helpompi ratkaisu on isorekursio (engl. isorecursion) 1 Ajatuksena on, että ohjelmoija merkitsee näkyviin paikat, missä on tarpeen tehdä muunnos tyypistä µα T (α) tyyppiin T (µα T (α)) ja takaisin käyttämällä kielen sisään rakennettavia konversiofunktioita unfold[µα T (α)]: µα T (α) T (µα T (α)) fold[µα T (α)]: T (µα T (α)) µα T (α) Nyt edellä esimerkkinä annettu sum-funktio jouduttaisiin kirjoittamaan seuraavasti: sum = µf : NumList Num λl : NumList case unfold[numlist] l of null = x 0 notnull = x let (a, r) = x in a + fr Toisaalta käytännön ohjelmointikielessä tämä ei ole niin ongelmallista, koska fold- ja unfold-funktiot voidaan niissä piilottaa osaksi kielen muuta sotkuisuutta. Esimerkiksi jos kieli vaatii, että tyyppirekursion tulee aina kulkea jonkin varianttityypin kautta, 2 voidaan varianttityypin käsittelyn sisälle piilottaa tarvittavat fold- ja unfold-operaatiot. 1. Etuliite iso- ei tässä viittaa kokoon vaan isomorfismiin. 2. Tämä ei ole kovinkaan vakava vaatimus, sillä käytännössä tyyppirekursio joka tapauksessa kulkee (lähes) aina variantin kautta. 3

Isorekursiiviset tyypit voidaan määritellä seuraavasti: α TyVars T, U ::= α µα T t, u ::= fold[t ] unfold[t ] Nyt myös tyypeille pitää määritellä korvausoperaattori T [α := U] ja vapaat muuttujat F V (T ). Määritelmät kirjoitetaan samaan tapaan kuin termeille (harjoitustehtävä). T = µα U Γ unfold[t ] : T U[α := T ] T = µα U Γ fold[t ] : U[α := T ] T unfold[t ](fold[t ] v) v 5 Ekvirekursio Ekvirekursiivinen lähestymistapa on kaikista simppelein ohjelmoijan kannalta, mutta vaatii varsin raskasta koneistoa tyyppijärjestelmään. Pääongelmana on, että tyyppitarkastimen pitää nyt päätellä, mihin fold- ja unfold-operaatiot kuuluvat, ilman ohjelmoijalta saatavaa apua. Ekvirekursiivisesti tyypitetty lambda-kieli voidaan määritellä seuraavasti: α, β, γ TyVars x, y, z Vars T, U ::= T U α µα T t, u ::= x tu λx : T t 4

Γ, x : T x : T Γ t : U 1 T Γ u : U 2 U 1 U 2 Γ tu : T Γ, x : T t : U Γ (λx : T t) : T U Tyypityssäännöt ovat siis samat kuin yksinkertaisesti tyypitetyssä lambda-kielessä lukuunottamatta applikaation sääntöä. johon on lisätty eksplisiittinen tyyppien yhtäläisyysvaatimus. Laskentasäännöt eivät muutu, joten niitä ei tässä tarvitse toistaa. Ekvirekursiivisten tyyppien yhtäläisyyden idea on avata µ-tyypit äärettömiksi tyypeiksi ja sitten verrata, ovatko nämä äärettömät tyypit samoja. Yksinkertaistaen siis: expand(α) = α expand(t U) = expand(t ) expand(u) expand(µα T ) = T [x := expand(µα T )] T U expand(t ) = expand(u) Käytännössä tämä on tietenkin käyttökelvoton algoritmi, sillä se vaatisi äärettömän tietorakenteen läpikäymistä. Brandt ja Henglein (1998) esittävät yhden mahdollisen algoritmin asian ratkaisemiseksi. Seuraavissa päättelysäännöissä Γ on tyyppiparien (joita merkitään T U) joukko. Γ T T Γ, T U T U Γ U T Γ T U Γ T 1 T 2 Γ T 2 T 3 Γ T 1 T 3 Γ µα T T [α := µα T ] Γ, T 1 T 2 U 1 U 2 T 1 U 1 Γ, T 1 T 2 U 1 U 2 T 2 U 2 Γ T 1 T 2 U 1 U 2 Päättelysäännöstöstä on mahdollista johtaa seuraavanlainen algoritmi: 5

Algoritmi 1 (Brandt ja Henglein (1998)). Syötteenä tyyppiparien joukko Σ sekä kaksi tyyppiä, tuloksena joko tosi tai epätosi. Sovelletaan järjestyksessä ensimmäistä sopivaa yhtälöä: S(Σ, µt.t, U) = S(Σ, T [t := µt.t ], U) S(Σ, T, µt.u) = S(Σ, T, U[t := µt.u]) S(Σ, T, U) = true (1) S(Σ, T T, U U ) = S(Σ, T, U) S(Σ, T, U ) (2) S(Σ, t, t) = true (3) S(Σ T, U) = false (1) jos (T, U) Σ (2) missä Σ = Σ {(T T, U U )} (3) t I Algoritmin (ja sen oikeellisuustodistuksen) taustalla on koinduktiivinen metodi, joka sivuutetaan tässä. 6 Rekursiivisten tyyppien seurauksia Esimerkki 2 (Nälkäiset funktiot). Tarkastellaan tyyppiä Hungry = µα Num α. Yksi mahdollinen tuohon tyyppiin kuuluva funktio on f = µf : Num Hungry λx : Num f. Tällainen funktio tarvitsee äärettömän monta argumenttia. Esimerkki 3 (Virrat). Hyödyllisempi on tyyppi NumStream = µα () (Num, α) joka palauttaa joka kutsulla luvun ja uuden virran: ones = µf : NumStream λx : () (1, f) nats = (µf : Num NumStream λx : Num λy : () (x, f(x + 1))) 0 6

Esimerkki 4 (Yksinkertaiset oliot). Määritellään tyyppi Counter = µα { get : Nat, inc : () α } Nyt voidaan määritellä tehdasfunktio createcounter = µf : { x : Num } Counter λs : { x : Num } { get = s.x, inc = λy : () f({ x = s.x + 1 }) } ja sen avulla olio createcounter { x = 0 }. Tällä oliolla on tosin kaksi puutetta: sillä ei ole tilasta riippumatonta identiteettiä (jokainen muutos luo uuden olion) ja alityypitys ei luonnistu. Kumpikin ongelma on ratkaistavissa identiteetti käyttämällä osoitintyyppiä, alityypityksestä puhutaan myöhemmällä luennolla. Mainittakoon vielä, että rekursiivisesti tyypitetty lambda-kieli on yhtä vahva kuin tyypittämätön lambda-kieli, eli siinäkin on mahdollista määritellä kiintopisteoperaattori muiden ominaisuuksien avulla. Viitteet Michael Brandt and Fritz Henglein. Coinductive axiomatization of recursive type equality and subtyping. Fundamenta Informaticae, 33(4):309 338, April 1998. Benjamin C. Pierce. Type and Programming Languages. MIT Press, Cambridge, MA, 2002. 7