Bevezetés az Ada típusrendszeréhez
A programozási nyelveknél a biztonság kérdése szorosan összefügg a típus fogalmával. Általánosságban elmondható, hogy egy nyelv annál biztonságosabb, minél több hibát ki tud szűrni már a fordítás közben. Egy jól megkonstruált nyelv ugyanakkor igyekszik minél jobban irányítani a programozót az adott nyelv sajátosságainak kihasználása felé. Persze egy feladat sokféleképpen megfogalmazható, és megoldható, de vannak olyan megoldások, amelyek a nyelv logikájába jobban illeszkednek mint mások. Tegyük fel, hogy egy feladat úgy kívánja, hogy korlátozzuk egy változó által felvehető lehetséges értékeket (szorítsuk meg azt). Ezzel egy olyan feltétel fogalmazódik meg, amelyet a program fejlesztése során sehol sem szabad figyelmen kívül hagyni. Az egyszerűség kedvéért tételezzük fel, hogy egy „órát szeretnénk csinálni” és elindítani, ahol a másodperceket egy integer (egész) típusú változóban szeretnénk tárolni. Feltesszük, hogy van egy ütemező mechanizmusunk, amely minden másodpercben kivált egy eseményt, aminek eredményeképpen a következő utasítás hajtódik végre: másodperc:=másodperc+1;
Nézzük, melyek azok a kérdések, amelyek felmerülnek: Kezdetben mennyi a másodperc változó értéke? Milyen értékeket vehet fel? A fenti értékadás teljesíti ezeket az elvárásokat? Mit tegyünk, ha valamiért mégis hibás érték kerül a másodperc változóba? Még számos fontos kérdés megfogalmazható, de először nézzük végig ezeket.
A másodpercek a mostani egyszerű feladatunkban egész számok 0..59-ig. Miért? Gondoljuk meg, ha nem ezt az intervallumot adjuk meg, hanem például az 1..60 intervallumot, akkor az egész perceket egy egészen érdekes formában tudnánk csak megfogalmazni. (Bár szintén 60 különböző értékről van szó, így a célnak teljesen megfelel ez a fajta megvalósítás is. Mégis, célszerű az első intervallumot használni.) Mivel az intervallum 0-ával kezdődik, és ez egy értelmes értéket reprezentál, célszerű ezt adni a változó kezdeti értékének. (Mondhatnánk, hogy kezdetben legyen -1 az értéke, és az első értékadásnál „behoznánk az intervallumba a változót”, de jelen esetben ez szükségtelen. Más esetekben ez lehet hasznos gondolat, pl. ha arra a kérdésre is szeretnénk egyszer választ adni, hogy az óra elindult-e már…)
Ha a 0..59 intervallummal szeretnénk dolgozni, akkor mi garantálja, hogy a változónk, ami egész típusú (integer), csak ezeket az értékeket veszi majd fel? Nyilván, ha egész számokról (integer) van szó, akkor szükség lenne egy elágazásra, ami pl. 0-ára állítja a változót, amint annak értéke meghaladja az 59-et. Ez egy jó megoldás, de egy átlagos karórán (mutatós vagy digitális) a perceket visszafelé is lehet pörgetni, és azok még percek maradnak… Tehát ez a megoldás még az adott helyzetben sem biztos, hogy elégséges, hiszen egy „órát” szeretnénk csinálni. Persze mondhatjuk, hogy oké tegyünk be még egy elágazást, ami minden esetben 0..59 közé helyezi a változó értékét, amennyiben csökkentjük azt. (Ez már bonyolódik…) Vagy magát az értékadást tegyük egy eljárásba, ami mindezen ellenőrzéseket elvégzi helyettünk, és végrehajtja az értékadást és valamilyen formában jelzi a hibát, amennyiben hibás értéket adnánk a változónak. Ez már egy egészen jó megközelítés, hiszen, ha direktbe nem változtatjuk a változó értékét, csak ezen az eljáráson keresztül, akkor ez egy elég magas fokú biztonságot eredményez. (Néhányaknak biztosan eszébe jutott a C#-os tulajdonság fogalom…) Azonban most gondoljunk más szempontokra is, amelyek szintén lényeges kérdéseket vetnek fel. Szükségünk van nekünk a teljes integer típusra a fealdathoz? Elég hatékony lesz ez a megoldás? Több változó esetén kényelmes mindegyikhez ilyen „biztonságos értékadást” készítenünk? Ezekre a kérdésekre Ada programozási nyelven egyetlen sorral válaszolhatunk. És ebben az erős típusrendszer van segítségünkre, amely a nyelv alapjait képezi. A számos beépített típus mellett mi is készíthetünk sajátokat. (Akár a gépi reprezentáció szintjéig lemenve.) A következőkben néhány nagyon egyszerű példát mutatunk, amely rendkívül hasznos, és gyakran használt.
Az integer típust már ismerjük, ez egy intervallum, amely egész számokat tartalmaz és integer’first..integer’last intervallumot jelenti. (Amit látunk az ’ jel után (a ’first, és ’last) azok a típusosztályhoz tatozó attribútumok, amelyekkel információt szerezhetünk a beépített és általunk definiált típusokról és változókról. A példaprogramokban számos attribútum használatát bemutatjuk, itt azonban nem foglaljuk össze őket, hiszen ennek pontos leírása számos irodalomban megtalálható. Ajánljuk a referenciakönyv tanulmányozását!)
A natural (al)típus a 0.. integer’last intervallumot jelenti, míg a positive az 1.. integer’last intervallumot. Ha egy positive típusú változót deklarálunk, akkor annak nem adhatjuk értékül a 0 értéket. (Fordítási figyelmeztetést kapunk, valamint a futás közben fellép CONSTRAINT_ERROR kivétel is, amely a típusinvariáns megsértésére figyelmeztet.) Ezek a kivételkezelő részek automatikusan generálódnak, a programozónak nem kell kézzel megírnia őket. (Ez természetesen némi ~20% hatékonyságromlás eredményez, de ez biztonságkritikus szoftverek tervezésénél az esetek többségében megéri!) De készíthetünk egy teljesen új típust is.
type másodperc_intervallum is range 0..59; --típusdefiniálás
másodperc:másodperc_intervallum; --deklarálás
Így a változónknak tényleg csak helyes értéket adhatunk értékül, különben kivétel lép fel. Azonban ezen a módon az elágazást még mindig nem kerültük el. (Gondoljunk bele.) Azonban a nyelvben van például moduló (maradék) típus definiálására is lehetőség, amelyen a matematikai műveletek moduló működnek…
type másodperc_intervallum is mod 60;
Így tényleg a fenti értékadás kielégítő, miszerint: másodperc:=másodperc+1;
És itt mivel moduló számolunk nem léphet fel kivétel, hiszen nem sértünk meg semmit. (=> Nem generálódik kivételkezelő rész nagy valószínűséggel. => Hatékonyabb lesz a kód.)
Amikor erre ránéz egy programozó egyből tudja, hogy egy ilyen típusú változónak az értéke valószínűleg „körbe fog haladni”. Tehát a nyelvnek rendkívül nagy a kifejező ereje. Így kevesebb (dokumentálást is, és ami a fontosabb) hibázási lehetőséget rejt magában.
Számos bonyolultabb példát is nézhettünk volna, de ez az egyszerű is azt a szemléletet tükrözi, miszerint a hatékony programozás Adában a nyelv ismeretét, és a nyelvi elemek helyes használatát jelenti, és nem a bitekkel, és a memóriával való játszadozást. Egy helyesen Adául megírt kód hatékony is lesz. (Természetesen van lehetőség, a bitek szintjéig lemenni, és új típust létrehozni, majd a későbbiekben visszatérünk erre!) A típusok széles köre lehetőséget biztosít, hogy több esetenként magasabb absztrakciós szinten/szinteken dolgozzunk. (Megjegyezzük, hogy két absztrakció közül igen nehéz eldönteni, hogy melyik az absztraktabb…)
A következő példaprogramok néhány típus definiálását, és deklarálását szemléltetik. A Bevezetés teljes témaköre jórészt a típusok megismertetéséről szól.
Megjegyezzük, hogy az Ada 2005-ös szabványa már támogatja a névtelen típusokat is, azaz nem kell feltétlenül a type kulcsszó után definiálnunk azokat, ezt a deklaráláskor is megtehetjük. Mindenképpen ajánljuk a Linkek-nél hivatkozott diasor olvasását, amely egy összefoglalót is tartalmaz a típusokról magyar nyelven. Ezt itt nem kívánjuk megismételni, inkább csak hozzáteszünk, illetve egy más színezetet adunk neki.
Programokódok: