Ohjelmointikieli TIE-20306 Principles of Programming Languages Syksy 2017 Ryhmä 19 Juho Kärnä Ville Mäntysaari
1. Johdanto D on yleiskäyttöinen, strukturoitu, staattisesti tyypitetty, käännettävä ohjelmointikieli järjestelmien ja sovellusten koodaamiseen. D tukee imperatiivista ja jossain määrin funktionaalista ohjelmointiparadigmaa sekä olio- ja rinnakkaista ohjelmointia. Kielessä on C:n kaltainen syntaksi, ja kieli onkin suunniteltu sillä periaatteella, että saman näköinen koodi tekee saman asian C:ssä ja D:ssä. Siirtyminen C:n kaltaisista kielistä on näin tarkoitus tehdä mahdollisimman helpoksi. D:n tavoitteena on olla tehokas, kuten C:nkin, mutta tarjota silti mahdollisimman paljon tulkattavien kielten helppokäyttöisyyttä. D:ssä onkin useita käyttöä helpottavia ominaisuuksia, kuten automaattinen tyyppien päättely ja roskienkeruu. Helppokäyttöisyyden ja tehokkuuden lisäksi D:n yksi tärkeimmistä suunnitteluperiaatteista on turvallisuus. 2. Tyypit D on staattisesti tyypitetty kieli. Muuttujan tyypiksi voidaan kuitenkin antaa auto, jolloin kääntäjä päättelee muuttujan tyypin. Kielen perustyypit ovat aina samankokoisia riippumatta järjestelmästä, paitsi real, joka on niin suuri kuin mahdollista kyseisellä järjestelmällä. D ei ole vahvasti tyypitetty kieli. Implisiittinen tyyppimuunnos tyyppien välillä on sallittu, mikäli muuttujan arvon tarkkuus ei kärsi (liukulukujen välillä muunnos on sallittu myös arvojen tarkkuuden kärsiessä). Myös eksplisiittinen tyyppimuunnos on sallittu. D kielessä käytetään arvosemantiikkaa käsiteltäessä perustyypin muuttujia ja viitesemantiikkaa käsiteltäessä olioita. Jos halutaan antaa perustyypin muuttuja funktiolle viiteparametriksi, täytyy käyttää funktion määrittelyssä parametrin yhteydessä ref avainsanaa. Kielessä on käytössä osoittimia. D sisältää myös immutable- avainsanan, jota seuraava muuttuja voidaan asettaa vain kerran. Kielessä on myös avainsana const, joka on paikallinen määre. Esimerkiksi funktio voi luvata olla muuttamatta parametreja avainsanalla const. Const-parametreiksi voidaan antaa immutable muuttujia. D tukee sekä tietyn mittaisia tauluja, jotka tallennetaan stackkiin, ja dynaamisesti kasvavia tauluja, jotka menevät heappiin. Kielessä taulut ja myös hajautustaulut määritellään hakasulkuoperaattoreilla. Dynaaminen taulu varataan avainsanalla new. Alla esimerkki taulujen syntaksista ja staattisen ja dynaamisen taulun varaamisesta.
int [ 10 ] array; // Staattinen taulu int [] array = new int [ 10 ]; // Dynaaminen taulu int [string] hashmap; // Hajautustaulu D:ssä on sisäänrakennettuna tauluoperaatioita: Kaksia taulua voidaan lisätä yhteen alkioittain, ja taulu voidaan kertoa tai jakaa luvulla operaattoreilla *= ja %=. Merkkijonot ovat D:ssä aina Unicodea. Ne ovat määritelty immutable char-tauluina. Merkkijonot voivat olla useita riviä pitkiä. Niitä voidaan yhdistää operaattorilla ~. Lainausmerkkejä sisältäviä merkkijonoja voidaan määritellä sellaisenaan kahdella eri syntaksilla: (`merkkijono`) tai ( r merkkijono ). Alla on esimerkkikoodi. string s1 = `tämä on "testi"` ; // Merkkijono: tämä on testi string s2 = r "tämä on `testi`" ; // Merkkijono: tämä on `testi` writeln( s1 ~ s2); // Merkkijono: tämä on testi tämä on `testi` 3. Kontrollirakenteet D kielessä on samankaltaiset kontrollirakenteet kuin monissa muissa imperatiivisissa ohjelmointikielissä kuten funktiokutsut, for ja while silmukat, if-else-then rakenteet, goto rakenne ja switch-case rakenne. Lisäksi D kielessä on foreach- silmukka, jossa silmukkamuuttuja käy järjestyksessä läpi sekventiaalista rakennetta, kuten taulukkoa tai range-rakennetta. 4. Olio-ohjelmointi D kieli tukee olio-ohjelmointia. Luokka voi olla periytetty korkeintaan yhdestä luokasta ja interface- rakenne toimii abstraktina luokkana (abstraktissa luokassa vain esitellään metodit, jotka pitää toteuttaa siitä periytetyissä luokissa) ja se voidaan periyttää usealle luokalle. Kun luokassa ylikuormitetaan kantaluokan metodia, täytyy käyttää avainsanaa override metodin määrittelyssä. 5. Funktionaalinen ohjelmointi Vaikka D onkin pääosin imperatiivinen ohjelmointikieli, se sisältää mahdollisuuden tiettyihin funktionaalisen paradigman toimintoihin. Kielessä on esimerkiksi mahdollista määritellä sisäkkäisiä funktioita.
D sisältää avainsanan pure, joka määrittelee funktion puhtaaksi. Puhtaat funktiot eivät pääse käsiksi ohjelman tilaan ja palauttavat aina saman paluuarvon samoilla parametreilla. D:ssä on käsite vahvasti puhdas funktio, joka vastaa yleistä käsitystä funktionaalisen ohjelmoinnin puhtaasta funktiosta: funktio ottaa vain arvolla välitettyjä parametreja, eikä siten vaikuta funktion ulkopuoliseen tilaan. D:ssä on kuitenkin käytännön syistä mahdollista määritellä myös pure -avainsanalla määritellylle funktiolle viiteparametreja. Funktio voi muokata kyseisiä viiteparametreja, mutta ei tietenkään pääse ohjelman ulkopuoliseen tilaan käsiksi. Tällaista funktiota kutsutaan D:ssä heikosti puhtaaksi funktioksi. Kielessä on myös mahdollista antaa funktiolle parametrina funktioita. Avainsana function parametrina vaatii globaalin funktion. D:ssä on myös käsite delegate. Delegatet ovat pointtereita funktioon, jossa on mukana ylimääräistä tietoa funktion kontekstista. Funktioita ja delegateja ei voi sekoittaa keskenään, mutta globaali funktioreferenssi on mahdollista muuttaa delegaatiksi. D:ssä on mahdollista myös määritellä anonyymejä funktioita, sekä lyhyemmällä syntaksilla lambda-funktioita. Alla on esitelty D:n funktioiden syntaksia. // Anonyymi funktio auto f = ( int a, int b) { return a + b; }; // Lambda-funktio, kääntäjä muuntaa yllä olevaa koodia vastaavaksi. Muuttujien // f ja g tyyppi on delegate auto g = ( int a, int b) => a + b; // Funktio, joka ottaa lokaalin funktion parametrina int dotwice( int a, int delegate ( int ) f ) { return f(f(a)); }; Vaikkakaan laiska suoritus ei ole täysin funktionaalinen piirre, se esiintyy yleensä funktionaalisissa kielissä. D tukee laiskaa suoritusta anonyymien funktioiden muodossa. D:ssä jokainen lauseke voidaan implisiittisesti muuttaa delegateksi, joka palauttaa joko void tai lausekkeen tyypin. Funktiossa voidaan käyttää parametrin edessä avainsanaa lazy, joka korvaa eksplisiittisen delegate-määritelmän. Alla on esitelty D:n laiskasuoritusominaisuutta.
auto loggingenabled = false ; void log( lazy char [] delegate ) { if (loggingenabled) { writeln( delegate() ); } } void main() { // Tätä merkkijonoa ei koskaan rakenneta, sillä funktioon parametrina // annettavaa ( implisiittisesti muutettua ) delegatea ei koskaan kutsuta, log( "Log test." ); } 6. Rinnakkainen ohjelmointi D kieli tarjoaa rakenteita rinnakkaiseen ohjelmointiin. Jaettu data ja sen päivittäminen rinnakkaisten säikeiden välillä aiheuttaa suuren osan rinnakkaisen ohjelmoinnin ongelmista. D kielessä suositellaan käytettävän immutable muuttujia ja viestejä säikeiden välisiin kommunikointiin. Immutable muuttujia voidaan jakaa säikeiden kesken ilman synkronointia. Säikeiden välillä voidaan myös jakaa muuttuvaa dataa (joka on merkitty avainsanalla shared ) ja käyttää mm. mutex-lukkoja samanaikasen datan lukemisen ja päivittämisen välttämiseksi. 7. Kielen erikoisuuksia D:n ytimen muodostaa muistiturvallisten operaatioiden joukko SafeD. SafeD:n idea on tehdä ohjelmoinnista turvallisempaa, sillä muistin manuaalinen käsitteleminen on yksi virheherkimmistä ohjelmoinnin alueista. Muistia voi varata operaatiolla new, mutta pointteriaritmetiikka ja tarkastamattomat castit eivät ole sallittuja. Roskien keruu hoitaa varatun muistin vapauttamisen. Varaamattomaan tai jo vapautettuun muistiin ei pääse käsiksi. Tauluissa ja merkkijonoissa on indeksitarkastelut. SafeD:hen kuuluvat funktiot ovat merkitty @safe-annotaatiolla. Omia funktioita voi myös merkitä samalla tavalla, jolloin kääntäjä tarkastaa että funktio tekee vain sallittuja operaatioita. Jos funktio tekee kiellettyjä operaatioita, niin kuin joskus on tarpeellista tehdä, mutta käyttäjä uskoo tietävänsä mitä hän tekee, voidaan funktio merkitä @trusted-annotaatiolla. Safe-merkittyjen funktioiden sisältä voi kutsua vain @safe ja @trusted merkittyjä funktioita. Kaikki muut funktiot ovat oletuksena @system funktioita, joilla ei ole mitään rajoituksia. Yksi D:n tärkeimmistä ominaisuuksista on Uniform Function Call Syntax (UFCS). UFCS tarkoittaa, että mitä tahansa globaalia funktiota voidaan kutsua kuin jäsenfunktiota. Argumentittomasta funktiosta voi myös jättää aaltosulut pois. Tämä
mahdollistaa monimutkaistenkin funktioketjujen kirjoittamisen siten, että koodi on silti selkeää. D:ssä on myös mahdollista piilottaa tai jopa lisätä jälkeenpäin gettereiden ja settereiden toteutus, sillä luokassa @property-annotaatiolla merkittyjä funktioita voidaan käyttää suoraan kuin kenttiä. Alla on esitelty UFCS:sää. // f ja g on määriteltyjä globaaleja funktioita jotka ottavat yhden kokonaisluvun parametrina ja palauttavat kokonaisluvun. int a = 3 ; // Nämä kaikki tekevät saman asian. Viimeinen tapa on siitä vaarallinen, että // f:ää ja g:tä ei tunnista suoraan funktioksi. int b = f(g(a)); int c = a.f().g(); int d = a.f.g; class Test { int number = 0 ; int timesset = 0 ; int timesget = 0 ; @property int number(){ timesget++; return this.number; } @property void number( int value ){ timesset++; this.number = value; } } Test olio = new Test(); olio.number = 5 ; // Tämä on sallittua. Nyt olion timesset on 1. D kielessä on sisäänrakennettu mahdollisuus yksikkötestaukselle. Yksikkötestejä voi kirjoitaa unittest koodiblokin sisään, joissa assert komennolla voi testata toiminnallisuutta. Kielessä on myös rakenteita sopimussuunnittelun toteutumisen tarkistamiseksi.
Lähteet 1. Yleistieto - https://dlang.org/ 2. Logo - http://www.sukimashita.com/d/