A Sather programozási nyelv

Adattípusok, változók, kifejezések



Sather nyelven írt programokban az adatokat objektumok tárolják, mindegyik előre rögzített típussal, amely meghatározza, hogy milyen műveletek hajthatók végre rajtuk. Minden típus egy osztály (a beépített alaptípusok is!), mégpedig a $OB típus altípusa. Minden változónak deklarálni kell a típusát (vagyis hogy milyen típusú objektumokat tartalmazhat).

Az osztályok nevei csupa nagy betűsök, az absztrakt osztályok azonosítója ‘$’ jellel kezdődik. A változók, eljárások, iterátorok nevei kisbetűsök, az iterátorok (ld. később) nevei után mindig egy ! jelnek kell állnia.

Beépített adattípusok, deklarációk

A beépített logikai, szám- és szöveges típusoknak van ugyan speciális nyelvi támogatása (pl. hogy olyan értékekkel inicializálhatjuk őket, mint pl. 5 vagy "hello"), azonban azok is általános osztályként vannak definiálva, és a standard könyvtár részét képezik. A különbség csak annyi, hogy a nem beépített típusú változók mindig referenciák, tehát minden ilyen típusú objektumot (a JAVA-hoz hasonlóan) dinamikusan kell létrehoznunk a heap-en explicit konstruktor hívásokkal, statikusan ilyen objektumok nem jönnek létre.

A beépített típusok közül az INT osztály egész számok, a FLT osztály lebegőpontos valós számokat, a BOOL osztály logikai értékeket, az STR osztály pedig sztringeket reprezentál. Ilyen típusú változókat deklarálhatunk (és megadhatjuk a kezdeti értékeiket is) például így:

a:INT; b:INT := 5; c:FLT := 3.0; d:STR := "foo";

Minden explicit módon nem inicializált változó (a fenti példában az ’a’ ilyen) alapértelmezés szerinti kezdőértéke ‘void’, amely referencia objektumok esetén a NULL pointer, egészek esetén 0, valósak esetén 0.0, logikai értékek esetén pedig false (hamis).

Ezeken az osztályokon a szokásos műveletek (operátorok) meg vannak valósítva. Például a szám típusok összeadása, sztringek konkatenációja (mindkettőt + operátor jelöli), vagy a szokásos logikai műveletek (and, or, not):

a:STR := "hel"; b:STR := "lo"; c:STR := a + b;                -- c = "hello" e:INT := 5; f:INT := 7; g:INT := e + f;                -- g = 12 compare:BOOL := e > f;         -- compare értéke false lesz

Megjegyezzük, hogy a nyelvben van komplex típus is (CPX), amely ún. immutable (változtathatatlan) osztályként van megvalósítva (ld. 3.4. és 6.3. fejezetek). Egy komplex szám reprezentációja a valós és képzetes részeiből áll (két FLT típusú attribútum). A szokásos aritmetikai műveletek ezen típusra is meg vannak valósítva. Például:

b:CPX := #(2.0,3.0); d:CPX := #(4.0,5.0); c:CPX := b + d;

A fenti deklarációkban a # jel a CPX típus konstruktorának meghívását jelenti (ld. később).

Ha egy deklaráció vezérlési folyamatban (pl. ciklusban, ld. később) szerepel, akkor a változó nem inicializálódik minden esetben, csak az első alkalommal, kivéve ha ez explicit meg van adva. Például:

compute is   loop 3.times!;     a:INT;     a := a + 3;     #OUT + a + "\n";           -- Kiírás: 3, 6, 9   end; end;
compute is   loop 3.times!;     a:INT := 15     a := a + 3;     #OUT + a + "\n";           -- Kiírás: 18, 18, 18   end; end;

Értékadások

Az értékadások formája megegyezik a deklarációkban szereplő kezdeti értékadások szintaktikájával. Például:

a:INT := 15 a := a + 3;

Operátorok

A Satherben minden operátor valójában függvény és létezik is függvény megfelelője. Ezáltal az operátorok kifejtése és felüldefiniálása a szokásostól teljesen eltérő módon zajlik: az infix matematikai operátoroknak is megvan az eljárás megfelelője, amit ténylegesen jelent (pl a+b ::= a.plus(b)). Hasonlóan az értékadásnak és értéklekérdezésnek is van implicit függvény definiálva ( a := b ::= a.set(b.get( )) ). A C nyelvben sokat kritizált értékadó operátor (‘=’) itt az Algol 60-ból ismert alakban (‘:=’) szerepel.

Immutable objektumok

A nyelvben léteznek ún. immutable (változtathatatlan) objektumok, melyek értékeit csak készítéskor lehet beállítani. Ha egy ilyen objektumot szeretnénk módosítani, azt csak annak lemásolásával – új objektum létrehozásával lehet. Ezek használata hatékonyság növelésre ajánlott (a beépített és más kis méretű típusok ilyenek). Egyrészt többnyire a stacken tárolódnak, ezért nincsen heap kezelési többletköltség, és nem a szemétgyűjtő rendszernek kell az általuk lefoglalt területeket felszabadítania. Ezenkívül nem tárolnak plusz információkat a típusra vonatkozóan, így kevesebb helyet foglalnak.

Kis osztályok esetén (mint pl. a CPX, a komplex számok típusa), ezen szempontok együtt jóval előnyösebbé tehetik az immutable implementációt, nagy objektumok esetén azonban sokkal költségesebb lehet, mert minden módosítás másolást igényel. Az immutable objektumok kezelésének hatékonysága nagyban függ attól, hogy mennyire „ügyes” a használt C fordító, és ebben a vonatkozásban a gcc nem túl jó.

Immutable osztályok létrehozásának lehetőségeit részletesebben a 6.3.1. fejezetben mutatjuk be.

Típuskövetkeztetés

A kényelem kedvéért a Sather nyelvben mód van statikus típuskövetkeztetésre a környezet alapján. Ez több helyen is használható, ahol a típus statikusan eldönthető. Például új egyed létrehozásakor a változó típusából lehet tudni, mely osztálynak is szeretnénk egy új elemét elkészíteni, ezért az osztály nevét az értékadás mindkét oldalán felesleges feltüntetni. Például:

a:POINT := #(3,4);       -- Ekvivalens ezzel: a:POINT := #POINT(3,4);   a:POINT; a := #(3,4);             -- Ekvivalens ezzel: a := #POINT(3,4);

Hasonlóan alkalmazható deklaráció során is, ha értékadás is szerepel, amelyből a típus meghatározható. A deklaráció és az értékadás ilyen kombinációját ::= operátorral jelöljük. Ez rendkívül gyakori a Sather kódokban. Például:

a ::= #POINT(3,4);       -- Ekvivalens ezzel: a:POINT := #POINT(3,4); b ::= a;                 -- b is POINT típusú lesz c ::= 3;                 -- Ekvivalens ezzel: c:INT := 3;   p1 ::= #POINT(3,5); p2 ::= #POINT(4,5); p3 ::= p1.add(p2);       -- p3 is POINT típusú lesz

És természetesen függvényhíváskor is, ha egyértelmű a helyzet:

func(a:POINT) is ...   -- lehetséges használata: func(#(3,5));

Bár ez már átláthatatlanabb kódot eredményezhet, ezért nem is ajánlják.

Változók típusa sosem konvertálódik magától (még az integer-char, integer-float, single-double precision, sublass-superclass esetekben sem!), azt mindig külön (explicit módon) meg kell adni. Nincs futás idejű eljárás felüldefiniálás (overload), azonos nevű eljárások közül fordítási időben eldönthető, hogy mikor melyikre hivatkozunk.

A nyelvben létezik absztrakt típus (type) is: egy olyan eljáráshalmaz, amelyhez nem tartozik megvalósítás és több konkrét típusra (class) is vonatkozhat (részletesebben ld. később).

A Satherben megpróbálták az automatikus (implicit) kódok létét minimálisra csökkenteni, ezért nincsenek ideiglenes objektumok.

Satherben is van lehorgonyzott típus féleség (SAME), úgy mint Eiffelben (LIKE):

class Valami is   create Valami:SAME; end; -- Valami

Itt a SAME olyan típust jelent, mint maga az objektum, de például örököltetünk belőle, akkor már ott az örököst fogja jelenteni.