TIE-20306 PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli Seminaariesitelmä ryhmä 24 Markku Ahokas Jani Kuitti
i SISÄLLYSLUETTELO 1. YLEISTÄ EIFFELISTÄ... 1 1.1 Historia ja tausta... 1 1.2 Pääperiaatteita... 1 2. KIELEN RAKENNE... 2 2.1 Syntaksi... 2 2.2 Näkyvyysalue, silmukka ja kontrollirakenteet... 2 2.3 Sopimussuunnittelu ja poikkeukset... 3 2.4 Kääntäminen ja roskienkeruu... 4
1 1. YLEISTÄ EIFFELISTÄ 1.1 Historia ja tausta Ranskalainen Bertrand Meyer kehitti Eiffel nimisen olio-ohjelmointiin pohjautuvan ohjelmointikielen sekä yrityksen Eiffel Software vuonna 1985. Siitä lähtien Meyer ja hänen yrityksensä, joka on nykyään nimellä Interactive Software Engineering (ISE), on kehittänyt Eiffeliä ja 1986 tuli ensimmäinen toteutus kielestä. Vuonna 2005 Eiffelistä tuli ISOstandardoitu ohjelmointikieli. Kielen suunnittelun taustalla on pyrkimys parantaa ohjelmistojen toimintavarmuutta ja tehdä uudelleen käytettäviä moduleita. Koska Meyer on sopimussuunnittelun ja avoinsuljettu-periaatteen keksijä, ovat nämä periaatteet luonnollisesti olleet vaikuttamassa kielen kehittelyyn. Myös muita periaatteita kuten esim. DRY (don't repeat yourself) ovat olleet vaikuttamassa kielen suunnittelussa. 1.2 Pääperiaatteita Eiffel on käännettävä kieli, eli kääntäjällä käännetään koodi käyttöjärjestelmässä ajettavaksi sovellukseksi. Kääntäjiä löytyy useammalle käyttöjärjestelmälle kuten esim. Windows, Macintosh ja Linux. Kielenä Eiffel on imperatiivinen. Tämä tarkoittaa, että koodin käskyt ajetaan ohjelmoijan määrittelemässä järjestyksessä. Yleisesti ottaen käskyt ajetaan siinä järjestyksessä kuin ne ovat kirjoitettu, mutta kontrollirakenteet toki muuttavat käskyjen suoritusjärjestystä. Koodista pystyy kuitenkin imperatiivisessä kielessä lukemaan missä järjestyksessä käskyt ajetaan, mikä ei päde esim. funktionaalisessa kielessä. Eiffelissä kaikki tyypit ovat luokkia ja käytetään staattista tyypitystä. Tämä tarkoittaa, että muuttujien, parametrien ja metodien paluuarvojen tyyppejä tarkistetaan käännöksen aikana. Jos tyypit eivät ole yhteensopivat toistensa kanssa saada tämä virhe selville jo käännöksen aikana, kun taas dynaamisesti tyypitetyssä kielessä tyyppivirheet tulevat esiin vasta sovelluksen ajon aikana. Myös atomiset tyypit kuten kokonaisluku ja liukuluku ovat luokkia. Tavallisesti määritellyn luokan instanssille käytetään viitesemantiikkaa (class luokan_nimi). Jos taas käytetään expanded class luokan_nimi luokan määrittelyssä käytetään arvosemantiikkaa ja esim. muuttujan arvo on itse olio eikä viite olioon niin kuin viitesemantiikassa. Olio voi myös olla komposiitti, eli se voi sisältää toisen olion ja samalla myös viitteen toiseen ulkopuoliseen olioon. Esim. olio A sisältää olion B (aliolio) sekä viitteen olioon C joka on A:n ulkopuolella.
2 2. KIELEN RAKENNE 2.1 Syntaksi Eiffelissä kaikki ohjelman toiminnallisuus toteutetaan luokkien avulla. Luokat koostuvat yleensä luokan nimestä, inherit-, create- ja feature-osiosta. Nämä osiot erotellaan toisistaan avainsanojen avulla. Kukin osio alkaa aina jollain edelle mainituista nimistä ja päättyy -sanaan. Eri rivien sisennyksillä ei ole merkitystä. Inherit-osiossa voidaan määrittää, mistä luokista kyseinen luokka periytyy. Periytyvän funktion toiminnallisuus voidaan myös määrittää uudelleen käyttämällä redefine-avainsanaa. Create-osio sisältää funktion nimen, jota käytetään luokan rakentajana. Tämä ei ole kuitenkaan pakollinen osio. Jos create-osiota ei ole, ohjelma käyttää jokaisen luokan mukana automaattisesti luotavaa default_create-funktiota, joka ei tee mitään olion luontivaiheessa. Feature-osio sisältää kaikki luokan muuttujat ja funktiot. Funktion sisältää kaiken toiminnallisuutensa do-osion sisällä. Eiffelin funktiossa ei käytetä tavallisesta poiketen return-lausetta funktion arvon palauttamiseen, vaan palautettava arvo tallennetaan Result-muuttujaan, jonka arvo palautetaan funktiosta poistuttaessa. Funktiolle on myös mahdollista luoda paikallisia muuttujia. Tämä tehdään lisäämällä local-avainsana ennen do-osiota ja listaamalla alle kaikki halutut muuttujat. Funktiossa on myös mahdollista korvata do-sana once-sanalla, jolloin funktiosta tulee vain kerran suoritettava. Tällöin funktion ensimmäisellä suorituskerralla otetaan funktion paluuarvo talteen ja palautetaan se aina kun funktiota kutsutaan. Esimerkki luokasta: class HELLO feature message do print ("Hello world!%n") 2.2 Näkyvyysalue, silmukka ja kontrollirakenteet Eiffelissä kaikki oliot voivat lukea toistensa muuttujien arvoja. Oliot eivät kuitenkaan voi muokata toistensa muuttujien arvoja. Jos muokkaaminen halutaan kuitenkin sallia, voidaan tällöin luoda assign-funktio, jolloin luokan ulkopuolelle muuttujan arvon muuttaminen näyttää tavalliselta sijoitusoperaatiolta. Esimerkkinä muuttuja := uusi_arvo.
3 Eiffel sisältää C++:n while-silmukkaa vastaavan silmukkarakenteen. Ainoana erona on, että silmukkaa suoritetaan niin pitkään, että kontrollimuuttujalle määrätty vaatimus toteutuu. Silmukkarakenteeseen voi lisätä myös kohdat variant ja invariant. Variant arvon täytyy pienentyä jokaisella silmukan suorituksella vähintään yhdellä ja invariantin täytyy pysyä samana. Näillä voidaan varmistaa, ettei muodosteta päättymätöntä silmukkaa. from n := 0 until n >= 5 loop --... n := n + 1 Toisena silmukkarakenteena on across-silmukka, jolla voidaan käydää läpi erilaisia listarakenteita. Jotta tätä silmukkaa voitaisiin käyttää, täytyy silmukkarakenteen jokaisella alkiolla olla next-funktio ja itse rakenteella first- ja last-funktiot. across list as it loop -- Saadaan iteraattorin osoittama alkio list-oliosta it.item Kieli sisältää perinteisen if-rakenteen sekä inspect-rakenteen Jälkimmäisessä voidaan esimerkiksi tutkia jonkin muuttujan arvoa ja valita suoritettava koodi sen arvon mukaan. 2.3 Sopimussuunnittelu ja poikkeukset Sopimussuunnittelu on olennainen osa Eiffeliä ja on sisäänrakennettu kieleen. Yleensä kääntäjässä voi säätää milloin sopimussuunnittelua käytetään ja missä määrin eli millä tasolla. Sovelluksen tuotantoversiossa kannattaa jättää sopimussuunnittelu pois koska sen hyöty on virheiden löytäminen kehitystyön aikana. Avainsanalla require määritellään esiehto, joka on totuusarvon palauttava lauseke. Esiehto määritellään ennen do-osiota. Avainsanalla ensure määritellään samalla tavalla jälkiehto. Tämä tehdään do-osion sisällä ja sekä esi- että jälkiehdoille määritellään myös oma tagi. Luokkainvariantti määritellään avainsanalla invariant feature-osion ulkopuolella ja samalla tavalla kuin esi- ja jälkiehdot. Poikkeuskäsittely- eli rescue-lohkoon siirrytään, jos funktion kutsuja rikkoo funktiolle asetettua esiehtoa tai funktion tulos rikkoo sille asetettua jälkiehtoa. Tällöin voidaan palata funktion suoritukseen avainsanalla retry. Uudelleen yrityksille voidaan asettaa jokin yläraja ja sen ylityttyä palataan funktiosta takaisin sen kutsujaan. Tällöin poikkeuksen käsittely jää funktion kutsujan hoidettavaksi.
4 Esimerkki sopimussuunnittelusta ja poikkeuskäsittelystä: require esiehdon_tagi: a > 0 do -- Koodia joka tekee jotain esim. lisää muuttuja a:n arvoa ensure jalkiehdon_tagi: a < 10 rescue -- Käsitellään poikkeusta retry -- Retryllä voidaan suorittaa do-osio uudelleen 2.4 Kääntäminen ja roskienkeruu Eiffel mahdollistaa myös muiden ohjelmointikielien käytön koodin sisällä. Ainakin C- kieli on suoraan tuettu niin, että Eiffel-funktion rungon voi koodata C-kielellä. Voidaan käyttää C-koodia esim. lyhyille matalan tason operaatioille. Useimmat Eiffel-kääntäjät generoivatkin ensin C-koodia, jota sitten käännetään C-kääntäjällä. Tällä tavoin voidaan optimoida koodia C-kääntäjällä ja saada siirrettävyyttä eri käyttöjärjestelmille. Aina ei kuitenkaan generoida C-koodia eikä varsinaista suoraa kytkentää ole C-kieleen. Nykyään jotkut kääntäjät osaavat myös tehdä CIL (Common Intermediate Language) -koodia tai Java bytecode -koodia välivaiheena. Eiffel käyttää roskienkeruuta, eli vapauttaa automaattisesti olion varaamaa muistia, kun siihen ei enää viitata. Vaikka roskienkeruu on tyypillistä tulkattaville kielille, löytyy Eiffelin lisäksi myös muitakin käännettäviä kieliä, joilla on roskienkeruuta. Eiffelin roskienkeruu on siitä hyvä, että se ei pelkästään vapauta muistia omaan käyttöönsä vaan palauttaa vapautettua muistia käyttöjärjestelmälle. Näin vapautettua muistia voidaan käyttää jopa muille sovelluksille, mikä on hyödyllistä varsinkin sovelluksen pitkäkestoisessa käytössä.