A Smalltalk programozási nyelv

A nyelv felépítése

Mint már korábban volt szó róla: a Smalltalk egy teljesen objektum-orientált nyelv. Minden, amivel dolgozunk objektum, még a legegyszerűbb típusú adatok is. A nyelv egyszeres öröklődést enged meg, de az kötelező, vagyis valamilyen szinten az összes osztály egy közös ősosztályból származik (az Object osztályból). Az objektumok interakciójának egyetlen módja van: üzenetek küldése az objektumoknak. Ha küldünk egy objektumnak egy üzenetet, akkor a rendszer kiválasztja az üzenethez tartozó metódust, és azt végrehajtja. (Ez hasonló más objektum-orientált nyelvekben egy adott metódus megkereséséhez és végrehajtásához.) A metódusok kifejezések sorozatából állnak, amelyeket a rendszer sorban egymás után végrehajt. Másféle utasítás nincs, a vezérlési szerkezeteket is különleges objektumokkal oldhatjuk meg. A kifejezések pedig mindig objektumnak küldött üzenetek.

Lexikális elemek

A rendszer az ASCII karaktereket használja. Megkülönbözteti a kis- és nagybetűket, és ezek használatára ajánlást is ad. A globális változókat és osztályneveket nagybetűvel, minden mást (a lokális változókat, az osztály- és objektumszintű metódusokat) kis kezdőbetűvel írunk. Ha a név több szóból áll, akkor a további szavakat nagy kezdőbetűvel emeljük ki.

Literálok

Számok

Számliterálokat a következő alakban lehet írni: [-][<radix>r]<egészrész>[.<törtrész>][e[+|-]<kitevő>] értéke: (egészrész.törtrész)radix*(radixkitevő). A radix (számrendszer alapszáma) és a kitevő mindig tízes számrendszerben szerepel. Az előbbi tetszőleges szám lehet 2 és 36 között. Az alapban pedig a számokon kívül csak nagybetűk használhatók (A=10, Z=35). Példák: 42 16r5E 2r11e5 34e-5 8r23.45e-3. Bizonyos implementációkban nem lehet egy literálon belül megadni más (10-estől eltérő) számrendszert és exponenciális alakot. A számliterál típusa a következők alapján dől el:

  1. Float (lebegőpontos), ha van benne .<törtrész> (Függetlenül attól, hogy esetleg éppen egy egész számot ábrázol.)
  2. Fraction (hányados), ha a negatív kitevő miatt nem lehet egész az értéke
  3. SmallInteger, ha elfér az egész szám a gépi szóban
  4. LargeInteger, különben.

Látható, hogy a gépi szónál nagyobb számokat is kezelni tudja, ezért tetszőlegesen nagy számmal lehet számolni.

A Fraction egy kényelmesen használható típus, mert ha egy törtszámot írunk fel (pl. 2/3), valójában a két egész szám között végrehajtódik az osztás művelet, aminek eredményeként egy Fraction típusú objektumot kapunk.

Karakterek

A karakterliterálokat egy dollárjel vezeti be: $a az 'a' karaktert jelöli, a szóközt, $$ pedig a $-jelet. A sor végén elhelyezett dollárjel elvileg a sorvége-karaktert jelöli, de ez nem mindig van így (sőt, egészen furcsa eredményeket lehet kapni).

Stringek

A stringliterált aposztrófok közé kell írni. A stringen belüli aposztrófot annak megduplázásával jelölhetjük: 'Joe''s pub'. Az így létrejött objektumok a String osztály példányai, és karaktertömbként kezelhetők. Ennek ellenére nem leszármazottja a String osztály az Array osztálynak, csupán "testvére", mindkettőnek az ArrayedCollection osztály az őse (más implementációkban FixedSizeCollection).

Szimbólumok

A rendszerben definiált azonosítók (pl. osztályok, üzenetek, globális változók) nevei. Pl.: #Variable, #Object, #at:put:, #GREEN A szimbólumok a Symbol osztály példányai, amely a String osztály leszármazottja.

Tömbök

A Smalltalk speciális szintaxissal segíti literálok tömbjének definiálását:

#( <elem1> <elem2> ... ) Ezek egyszerűen az Array osztály példányai lesznek. A tömbök egymásba is ágyazhatóak. Példák:

A tömbelemek literálok lehetnek: szám, string, karakter, tömb - illetve amit nem tud az előző kategóriákba besorolni, azt automatikusan szimbólumnak tekinti. (Pl. az utolsó példában a symbol-t, vagy az utolsó előttiben a vesszőket.) Az utolsó példát tehát így értelmezi: #(42 $c #symbol 'ZB' #($a $b) #x). Így tehát változót nem lehet tömbliterálban elhelyezni. Ez alól kivétel a három konstans változó, amelyeket felismeri: true, false és nil. Így tehát #(true false nil var) eredménye #(true false nil #var) lesz.

Változók

A változók a Smalltalkban is csak referenciák, mint ahogy minden eleve objektumorientáltnak tervezett nyelvben megszokhattuk, hiszen ez teszi lehetővé a polimorfizmus teljes kihasználását. Ezenkívül fontos megjegyeznünk, hogy a változóknak nincs típusa, pontosabban minden változó típusa: objektumra (Object-re) mutató referencia. Ebből következik, hogy a változók deklarálásakor csak a nevüket kell megadnunk. A változók deklarálása kötelező, tehát nem használhatunk változót anélkül, hogy deklaráltuk volna.

A változóba tehát bármikor, bármilyen értéket beírhatunk, például az alábbi utasítássorozat hibátlan:

| i | i:=7. i:=Rectangle leftTop:10@10 rightBottom:20@20.

Alapértelmezésként a változók az UndefinedObject osztály nil nevezetű példányára mutatnak. Az UndefinedObject osztálynak ez az egy példánya létezik, minden inicializálatlan változó ugyanarra az objektumra mutat. Egy újabb érdekes ötlet a Smalltalkban, hogy ez az osztály is teljesen átlagos, az Object leszármazottja, így néhány metódus erre is meghívható, melyek közül a legfontosabb az isNil.

Változók deklarálása a következő formában történhet:

| vált1 vált2... |

Ezt a formát használjuk az osztályok adattagjainak deklarálásakor, a metódusok definiálásakor, és a szövegszerkesztő ablakokban is. A változók neve betű[betű/szám]* alakú lehet.

Változók fajtái:

  1. Példányváltozók:

    Az objektum részei. Élettartamuk az objektum élettartama. Egy osztály minden példányának megvannak a saját, külön példányváltozói. Ezeknek vagy nevük, vagy indexük van. A névvel ellátott példányváltozókra a nevükkel hivatkozhatunk, az indexeltek üzeneteken keresztül érhetők el. Indexeltek például a tömbök. Az indexelt változók száma különbözhet ugyanazon osztály különböző példányaiban, például a #( 1 2 3 ) kifejezéssel egy olyan tömböt hozhatunk létre, amelynek 3 indexelt példányváltozója van, a #( 'fel' 'le' ) pedig 2 elemű tömböt hoz létre, annak ellenére, hogy ezek ugyanazon Array osztálynak példányai. Indexelt példányváltozókat tartalmazó objektum létrehozásakor valamilyen formában meg kell adni az indexelt példányváltozók számát (például: f := Array new: 5). A size üzenet megadja az objektum indexelt példányváltozóinak számát. A példányváltozók csak az adott objektum számára érhetőek el.

  2. Időszakos változók:
    Egy művelet lokális változói, argumentumai és a tartalmazott blokkok argumentumai. Élettartamuk a művelet végrehajtási ideje. Blokk hívásakor érték szerinti paraméterátadás van.

  3. Osztott változók:
    Több objektum között vannak megosztva. Addig léteznek, amíg explicit módon nem töröljük őket. Ide tartoznak az osztályváltozók, amelyeket az osztály összes példánya elér, a globális változók, amelyeket minden objektum elér, és a készletváltozók (pool változók), amelyeket csak bizonyos osztályok példányai láthatnak. Ez utóbbiakat szótárakban definiálják. Nevüket nagybetűvel illik kezdeni. A készletbe a változó nevét és értékét tartalmazó Association osztályok kerülnek. A System az a készlet, ami a globális változókat tartalmazza. A készletváltozók névvel ellátott szótárakban vannak, amelyeket a Dictionary osztályból hozhatunk létre. Az osztályok definiálásakor megadott poolDictionaries részben ezen készleteket adhatjuk meg.

  4. Pszeudováltozók:
    A Smalltalk rendszerben előre definiált változók, melyek

    • osztályuk kizárólagos példányai, azaz az osztályukból más példány nem hozható létre. Ilyen például a nil, true, false, vagy
    • az aktuális objektumra hivatkozó változók: self, super.

Egyéb nyelvi elemek

Értékadás

Jele: := vagy <-, az adott implementációtól függően. A bal oldalon mindenképpen egy változónak kell szerepelni, amely a jobb oldalon álló (akár kifejezés eredményeként kapott) objektumra mutató referencia lesz. Az értékadást is kifejezésnek tekinti, értéke az értékül adott objektum, és ezért láncolni lehet: a:=b:=c

Megjegyzés

A megjegyzést idézőjelek között kell elhelyezni. Konvenció, hogy minden metódus elé (a neve után, de a törzs előtt) megjegyzésként leírjuk, hogy az mit csinál. Ugyanezt sajnos az általunk használt rendszerben osztályokra nem tehetjük meg, mert az ilyen megjegyzéseket szó nélkül törölte a rendszer.
(Osztályokhoz tartozó megjegyzést az osztályt reprezentáló objektumnak küldött #comment: üzenettel, és egy String típusú paraméterrel lehet megadni: MyClass comment: 'This is my class.')

Kifejezések elválasztása

A Smalltalkban utasításainkat ponttal kell elválasztanunk. A pont az utasítások elválasztására, és nem lezárására szolgál, tehát az utolsó utasítás után nem kell szerepelnie. Ennek ellenére használhatjuk ott is, de a rendszer figyelmen kívül hagyja. Ennek jelentősége a blokkoknál látható, ahol az utolsó kifejezésnek különleges jelentése van, s így a mögé helyezett pont nem befolyásolja azt.

Visszatérési érték meghatározása

Egy metódusban meghatározhatjuk annak visszatérési értékét, ha az egyik kifejezés elé a ^ jelet tesszük. Ekkor a további kifejezések nem kerülnek kiértékelésre, a metódus futása befejeződik. Ha egy blokkon belül helyezzük el, akkor a blokk futása is megszakad, így például egy ciklus belsejéből is ki lehet lépni. Ha nem határozzuk meg explicit módon a visszatérési értéket, akkor alapértelmezésként a fogadó objektumot adja vissza (self-et.)

Speciális jelek összefoglalása

:= vagy <- Az értékadás jele (az adott implementációtól függően).
"" Az idézőjelek közé írt szöveg megjegyzés.
'' Az aposztrófok közé írt szöveg string konstans.
$ A jellel karakter konstanst adhatunk meg, pl. $G vagy $@.
# A felhasználó által definiált szimbólumok, pl. osztálynevek, üzenetek, és tömbliterálok # jellel kezdődnek. Például: #Variable, #MyClass, #+, #at:put:, #(1 2)
. A Smalltalkban utasításainkat ponttal kell elválasztanunk.
; Az ugyanarra az objektumra vonatkozó, egymást követő metódushívások összevonhatók egy sorozatba a pontosvessző segítségével.
^ A metódus visszatérő értékének meghatározására szolgál.
|| A metódusok elején a lokális változók deklarálására szolgáló jelek.
! A metódusok lezáró jele.
[] Blokk definiálása
:| A blokkok elején a lokális változók deklarálására szolgáló jelek.