Standard típusok
Az Euclid a többi programozási nyelvhez hasonlóan utasításokból és adatleírásokból áll. Az adattípusok értékek halmazát,és rajtuk értelmezett műveleteket defíniálnak. Az értékek lehetnek konstansok, vagy változók.
Minden típusnak vannak ún. komponensei. (Lásd: Ada típus-attribútumok.) Egy komponens lehet egy másik típus, konstans érték vagy függvény. Minden típushoz az integer kivételével automatikusan hozzáadódnak deklaráláskor komponens típusok. Egy indexelt intervallumtípushoz például automatikusan kapcsolódik egy indextípus, ilyenkor az intervallumtípus változóinak indextípusú értéke mindig lekérdezhető, és minden azonos típusú változónál egyforma. Minden típushoz (az integert kivéve) tartozik két komponens. A T.size a T típus méretét adja meg tárolási egységekben, a T.alignment pegig azt, hogy mekkora tárolási egységhez van igazítva. Minden változónak, illetve konstansnak implicit van egy komponense, az x.itsType, ami az x típusát adja meg.
"Típusfüggő" komponensek pl.:
- T.IndexType : ha T tömb, a tömb index-típusa.
- T.ComponentType : ha T tömb, annak elemtípusa.
- Stb...
A típusok:
- Felsorolási típus: legalább két eleme kell, hogy legyen. A típusban felsorolt azonosítók nem szerepelhetnek máshol, csak abban a láthatósági tartományban, amiben a felsorolási típus deklarálva lett. Komponensek:
- T.first : az első elem a felsorolásban.
- T.last : az utolsó elem a felsorolásban.
- T.Succ(x) : az x-et követő elem. T.Succ(T.last) nem definiált.
- T.Pred(x) : az x-et megelőző elem a felsorolásban. T.Pred(T.first) nem definiált.
- T.Ord(x) : unsignedInt érték, x sorszáma a felsorolásban. A sorszámozás 0-val kezdődik.
- Részintervallum típus: ha A részintervalluma B-nek, és B C-nek, akkor A is részintervalluma C-nek. A komponensek ugyanazok, mint a felsorolási típusnál.
- Integer, signedInt, unsignedInt típus: egy változó típusa nem lehet integer, csak annak valamilyen résztípusa. A konstansok viszont lehetnek integer típusúak. Standard függvények:
- Abs(x)
- Odd(x) : Boolean típusú érték, igaz, ha x páratlan.
- Boolean: type Boolean=(False, True)
-
char: felsorolási típus, mely a lehetséges karaktereket tartalmazza. Karaktert úgy kell írni, hogy a karakter elé egy $ jelet teszünk. Pl.: $a az a karaktert jelenti. Speciális karakterek a $S, $T, $N, $$, $', ami sorban space, tabulátor, újsor, $ és ' karakter.
-
StorageUnit típus: a tárfoglalás alapegysége. Nincsenek megkülönböztethető értékei, és nincsenek műveletek definiálva rá. Egy ilyen típusú változó egyszerűen arra való, hogy lefoglaljon a memóriában egy adott méretű területet. Két komponense van:
- sizeInBits: a StorageUnit bitben megadott mérete.
- Address: function Address(A: array 0..n of StorageUnit) returns AddressType. Visszaadja az A(0) címét.
- AddressType: az integer típus egy előjel nélküli résztípusa, akkora, hogy beleférjen egy cím.
Ezek a típusok minden Euclid programban rendelkezésre állnak.
Példák :
type OneToHundred = 1 .. 100
type SubCol = red .. blue
type Classification = (confodential, secret, topsecret)
type Device = (disk, disp1ay, keyboard, printer, tape)
type Primary = red .. b1ue {the values of a Primary are red, green, and blue}
type screenPosition = 1 .. 525 {y coordinate for display screen}
Megjegyzés: valós típus nincs a nyelvben.
Mutató- és csoporttípusok(collections)
Mint tudjuk a deklarált változó a deklaráció blokkjában létezik, és azon kívül nem, ezért statikus változónak nevezzük. Ezzel ellentétben változókat dinamikusan is generálhatunk, anélkül hogy az a program struktúrájával kapcsolatban lenne. Ezeket a dinamikus változókat a standard New eljárással hozhatjuk létre. Az ilyen változókra mutatókkal hivatkozhatunk. Egy pointer típusba tartozó mutatók ugyanolyan típusú elemekre mutathatnak. A mutatókhoz három művelet létezik :
- a megegyezőség,
- a postfix * operátor, amely megadja a mutatott változót,
- az Index függvény, ami egy mutatót egésszé konvertál.
Egy dinamikus változónak egy csoport(collection) elemének kell lennie. A csoport egy változó, ami szinte úgy viselkedik, mint a tömb; egy elemét a pointerével kaphatjuk meg. Két fő különbség van: semelyik két csoportnak sem lehet két ugyanolyan pointertípusa, ezért egy csoportbeli elemet egy pointer pontosan meghatároz; illetve a tárkezelése a tömbnek és a csoportnak különböző. A csoportokra nincsenek műveletek, egyik csoportot nem lehet a másikhoz rendelni, csak egy csoport elemét lehet megkapni, illetve aktuális paraméterként továbbadni.
Képezhető collection típus is, amelynek elemei a collection változók.
Példák :
{ a collection objektum-tipusa a Stream parameterezett tipus lesz }
var myStreams: collection of Stream(unknown)
type StreamRef = ^myStreams { pointer tipus }
var userInput, userOutput: StreamRef
...
myStreams.New(userInput,keyboard) { a userInput pointer egy Stream(keyboard)-ra fog mutatni }
myStreams.New(userOutput,display) { itt letrehozunk es inicializalunk egy output-folyamot }
{ itt meg egy olyan (általános) collectiont, amiben Stream(any)-ra mutato pointerek csucsulnek }
myStreams.New(anystream, any)
{-----------------------------------------------------------------}
{ stringStorage meg nem tudja tarolt stringjei lehetseges hosszat }
var stringStorage: collection of string(unknown) in ioZone
{ a stringref viszont maxLength hosszusagu stringeket tartalmazo stringStorage-ra mutat }
type stringRef(maxLength: stringIndex) = ^stringStorage(maxLength)
Minden csoporthoz egy ún. zóna van hozzárendelve, ami a változók tárolását segíti.
A zóna egy modulváltozó, három speciális komponenssel:
- a storageBlocks nevű változó, ami rekordtípust tartalmazó csoport(collection), a theStorage: StorageUnit típusú mezővel;
- az Allocate(size, alignment: unsignedInt, var pointer: *storageBlocks) ;
- a Deallocate(pointer: *storageBlocks, size: unsignedInt) .
Ezeket a komponenseket nem kell exportálni, csak a standard
New és
Free számára szolgálnak. A zónának gépfüggőmodulváltozónak kell lennie. A
C csoport zónáját importálni kell minden olyan blokkba, ahol a
C.New-t, illetve a
C.Free-t meghívjuk. Ha a
C referenciaszámlálós, akkor a
C.zone-t importálni kell oda, ahol a csoport egy változóját értékül adják. A zóna nélküli csoport zónája a standard
SystemZone lesz, amit importálni kell, ha használni akarjuk.
Ha a counted alapszót írjuk a collection definíciójába, akkor annak minden változója hivatkozás-számlált lesz.
Ha egy csoport referenciaszámlálóval van ellátva, akkor az összes olyan változó törlődik amire nincs hivatkozás, illetve meg lehet adni egy konstanst, ami a maximális hivatkozásszámot tartalmazza, ami fölött már nem szabad később a változót felszabadítani. Az ilyen csoportra a Free nem alkalmazható. Egy C collection-változónak következő komponensei vannak:
- C.nil
- C.ObjectType
- C.zone
- C.Index(obj: *C)
- C.New(var p: *C)
- C.Free(var p: *C)
Van lehetőség arra is, hogy az allokáló-deallokáló eljárásokat magunk implementáljuk.
Típuskonstrukciók
A strukturált típusok komponens-típusokból épülnek fel, valamilyen struktuálási módszerrel (típuskonstrukció). Mindegyikre igaz, hogy ha a definíció elejére a packed kulcsszót tesszük, akkor az javaslatot jelent a fordítónak, hogy a lehető legkisebb helyet foglalják a változók.
Tömbök
Egy tömb azonos típusú elemek rendezett sorozata, melyet a komponensek és az index típusa, illetve a határai határoznak meg. Két standard komponense van egy T típusú tömbnek: a T.IndexType és a T.ComponentType. A két komponens a típus változóira is alkalmazható. Az Euclidban csak egydimenziós tömb deklarálására van lehetőség.
- Tömb típusok, pl.:
type Arr1 = array 0 .. 20 of signedInt
{OneToHundred mar definialva lett mint 1..100 (reszintv. tipus)}
type Arr2 = array OneToHundred of 0 .. 99
type Array2 - array Boolean of Color
type Nametable = array 0neHundred 0f String(50)
Rekordok
A rekord olyan típus, amely meghatározott számú, akár eltérő típusú mezőből áll. A mezők típusának definicióját a rekordon belül is meg lehet adni. A rekord komponensei konstansok vagy változók lehetnek. Egy rekord típusnak több variáns része is lehet. Variáns esetén egy egyszerű típusú konstanst kell szelektorként (tag) használni egy case szerkezetben, melyben a lehetséges variánsokat soroljuk fel. Mindegyik variáns részt egy case címke-vel láthatunk el, amely egy tag típusú manifest konstans vagy az otherwise szó. Egy ilyen rekord típusú változó deklarálásakor meg kell adni a tag-et, vagy konstansként, vagy ha nem tudjuk még, akkor az any kulcsszóval. A rekord típust például a string definiciójában is használják.
- Rekord típusok:
A rekordmezők kétfélék lehetnek: változók és konstansok. A mezők kaphatnak default kezdőértéket (mint az Adában).
type Date = record
var day: 1..31
var month: (Jan, Feb, Mar, ... )
var year: 1900..2100
end Date
A rekordoknak lehet variáns részük is.
Erre pl.:
type stream (dev: Device) = record
case dev of
display =>
var height: ScreenPos := ScreenPos.first
var nLines: 0 .. (ScreenPos.last)/8
end display
disk, tape =>
var file: FileHandle
var buffer: array 0 .. 255 of char
end disk
otherwise =>
end case
end stream
És végül itt a string típus definíciója:
type StringIndex = 1 .. stringMaxLength
type StringLength = 0 .. stringMaxLength
type string(maxLength: StringIndex) = record
var 1ength: 0 .. maxLen9th := 0;
var text: packed array 1 .. maxLength of char
end string
Modul típusok
lásd később
Gépfüggő rekordok
A gépfüggő rekordtípus korlátozott rekord típus. A gépfüggő rekordokban utasítást adhatunk a fordítónak az adatok reprezentálására. Az at kulcsszó után egy StorageUnit-ban megadott érték megadja, hogy hányadik tárolási egységbe kerüljön a mező értéke, a bits kulcsszó pedig azt, hogy mely bitekre. A rekord első StorageUnit-ja nullával kezdődik, az adott StorageUnit első bitje nulla. Ha nagyobb bit-számot adunk meg, mint amekkora a StorageUnit mérete, akkor a következő StorageUnit-on folytatódik. Hogyha a bits szimbólumot elhagyjuk, akkor a mező egész számú StorageUnit-ot foglal le, aminek a mérete a mező típusából lesz számolva úgy, hogy a mező beleférjen. A fordítónak kell ellenőriznie, hogy a mezők nem lesznek-e átfedőek, ill. hogy az érték ábrázolható-e a megadott helyen. Az ilyen rekordnak lehetnek konstans komponensei, de nem lehet paraméterezett. Az aligned mod a szerkezettel lehet kikényszeríteni azt, hogy úgy foglaljunk egy rekord típusú érték számára memóriát, hogy az első StorageUnit-jának a gépi címe 0 mod a legyen, ahol a-nak 2 hatványának kell lennie. Egy gépfüggő rekord csak gépfüggő modulban jelenhet meg. Az ilyen rekord definíciójában a machine dependent kulcsszó szerepel.
- Gépfüggő rekordok, pl.:
type float = machine dependent record
var sign (at 0 bits 0..0): 0 .. 1 := 0
var exponent (at 0 bits 1..7): -80#16 .. 7F#16 :=0
var mantissa (at 0 bits 8..31): 0 .. 0FFFFFF#16 :=0
end float
Azt hiszem, ezért nem definiálták előre a valós típust :) .
Halmazok
A Modula-2 illetve a Pascal halmaz-típusához hasonló.
A halmaztípus egy bázistípus feletti részhalmazokat definíál. A bázistípusnak egyszerű típusnak kell lennie. Műveletei: unió(+), különbség(-), szimmetrikus differencia(xor), metszet(*), eleme(in), részhalmaza(<=,>=). Standard komponense: T.BaseType megadja a T halmaz elemeinek a típusát.
- Halmaz típusok, pl.:
type SetA = set of 0 .. 20
Absztrakt adattípusok
Modul típusok
A modul típus a rekord típus általánosítása. A modul változókból, konstansokból, típusokból, eljárásokból, függvényekből áll.
Ezeknek a deklarációknak ugyanaz a formája és jelentése, mint a blokkbeli deklarációknak. Ezért a modul egy csomagként szolgál összefüggő objektumok gyűjteményéhez.
A modul abban is különbözik a rekordtól, hogy a benne foglaltak láthatósága szabályozható. Azaz kívülről csak azok a belső azonosítók érhetők el, melyek szerepelnek a modul export-listájában. Ha a modul egy T típusát exportáljuk, akkor alapesetben az a modulon kívül átlátszatlan lesz, azaz a rekord mezőire, a felsorolási típus konstansaira stb. kívülről nem hivatkozhatunk. Azonban ezeket elérhetővé tehetjük a with kulcsszó segítségével, és így meg lehet adni, hogy ezen típus változóinak lehessen-e értéket adni, vagy összehasonlítani. Ezt úgy lehet megoldani, hogy egy with kulcsszóval exportáljuk a típus után a :=, = szimbólumokat.
Ha egy T típust exportálunk egy M modulból, akkor a T a modulon kívül átlátszatlan marad, így az ilyen típusú változóra az összehasonlításon és az értékadáson kívül csak olyan műveleteket lehet használni, melyek egy M-beli rutinban definiálva vannak.
Hogyha T exportálásakor a with szimbólumot használjuk, a with után megadhatók az exportálandó műveletek. Ezenkívűl T mezőazonosítói, beleértve a standard-eket is, nem használhatók, ha nem szerepelnek a with listában. Egy modul elé kitehető a machine dependent szimbólum is, amely egyszerűen azt jelenti, hogy tartalmazhat más gépfüggő modul- és rekorddeklarációt, fix címeket a változódeklarációkban, kiterjesztett karaktereket a $ddd formában, típuskonverziókat, beleértve az implementációfüggő reprezentációjú típusokat, gépi kódú eljárásdeklarációkat.
Egy modul tartalmazhat inicializáló eljárást(initially kulcsszó), amely mindig végrehajtódik, ha a modultípus egy változója létrejön; továbbá lezáró eljárást(finally kulcsszó) is, amely a változó megszüntekor hajtódik végre. Fontos, hogy a (modul)változók a deklarálás sorrendjével ellentétes sorrendben szünnek meg. A modulban definiálhatunk egy invariánst is, ami feltételezhetően a modul változó élettartama alatt igaz, kivéve persze, ha a modul egyik eljárása éppen végrehajtódik. Természetesen a fordító csak akkor generál ehhez kódot, ha a checked opció be van kapcsolva. Az invariánshoz megadható egy absztrakt függvény, ami a modulon belüli változókat térképezi fel. Erre azért lehet szükség, mert egy modul típusú változó modulon belül különböző típusú változókkal van reprezentálva. Egy ilyen absztrakt függvény nem hívható meg az Euclidon belül. lásd még: Helyességbizonyítás nyelvi eszközei
A modulon kívül lévő azonosítók nem láthatók a modulból, hacsak nem szerepelnek az import-listán, vagy nem pervasive-ok.
Most következzen a szokásos példa, a verem típus megvalósítása:
type Stack (Stacksize: unsignedInt) = module
exports (Pop, Push)
var IntStack: array 1..StackSize of signedInt
var StackPtr: 0..StackSize := 0
procedure Push (X: signedInt) =
imports (var IntStack, StackPtr, StackSize)
begin
procedure Overflow = ... end Overflow
if StackPtr = StackSize then Overflow
else StackPtr := StackPtr+1;
IntStack(StackPtr) := X;
endif
end Push
procedure Pop (var X: signedInt) =
imports (var IntStack, StackPtr)
begin
procedure Underflow = ... end Underflow
if StackPtr = 0 then Underflow
else X := IntStack(StackPtr);
StackPtr := StackPtr-1
end if
end Pop
end Stack
...
var p, q: Stack(100) { letrehoztunk ket 100-as meretu vermet }
...
p.Push (20); { tipusmuveletek hivasa }
q.Push (330);
p.Pop (item);
A nyelv modulfogalma abban különbözik a Modula-2-étől, hogy itt a modul nemcsak egységbefoglaló szerkezet, hanem típus is. Ezzel valahol a Modula-2 modulfogalma és a modern objektum-orientált nyelvek osztályfogalma közt van (az Euclid-nál természetesen nincs szó objektum-orientáltságról). Ezek a modulok jó eszközül szolgálnak absztrakt adattípusok készítésére, mivel az exportlistát meg tudjuk úgy választani, hogy az adatmezők csak a típusműveletek által legyenek módosíthatóak.
Deklarációk és érvényességi körök
A konstans egy literális konstans vagy pedig egy olyan kifejezés, ami literális konstans helyén használható (kiértékelhető fordítási időben). A konstans értékét fordítási időben egyszerűen meg lehet határozni. Ezért konstans kifejezésben nem szerepelhet függvényhívás a beépítetteken kívül. A konstans kifejezések és a literális konstansok összefoglaló neve manifest constants.
Konstansokat a const szóval hozhatunk létre. Ha a konstans típusa strukturált, akkor az értékeket zárójelek között, vesszőkkel elválasztva sorolhatjuk fel.
Minden változó egy "fő" változó részeként értelmezhető. Egy változó része egy másiknak, ha az egyik értékének megváltozása a másikét is megváltoztatja, és az egyik változó értékének helye a másik leszűkítése. Két változó elfedi egymást akkor és csak akkor, ha egyik része a másiknak.
Egy teljes változó saját maga főváltozója, és soha sem része más teljes változónak.
Egy változó komponensét egy másik változót követő szelektor jelöl ki. A szelektor formája a változó típusától függ.
Egy tömbváltozó komponensét a változót követő indexszel jelölhetjük ki. Az indexelt változó a tömbváltozó része.
A mező kijelölők jelölik ki a rekord komponensének, a modul változójának, vagy egy változó típusának a formális paraméterét.
Ha a p egy pointer, és a C csoport elemeire mutat, akkor a p* egy hivatkozás, amely a p által mutatott elemet adja meg. Ez a hivatkozás a csoportváltozó(collection) része.
Egy változót használata előtt deklarálni kell. Mivel rekurzív típusdefiníciónál ez lehetetlen, ezért ilyenkor a forward parancsot kell a típusdefiníció helyére írni, és annak később egy deklarációban szerepelnie kell. A forward és a teljes definíció között illetve magában a definícióban a típust csak formális paraméter deklarálására vagy egy csoport(collection) tárgytípusaként lehet használni. Ha a típus paraméterezett, akkor a formális paraméterlistának csak a forward definícióban szabad megjelennie.
A változók deklarációjakor megadható egy kezdőérték, ami egy értékadással ekvivalens, mely a deklaráció után azonnal végrehajtódik.
Deklarációkor megadható a változó tárolási helye is.(lsd. Gépfüggő rekordok.)
Példák:
const mi := -1
const cc: Color := red
const idx: array -1..4 of unsignedInt := (8,0,2,3,1,5)
var p, q: Boolean := False {p is, q is False}
{ arr-nak lathato tombnek kell lennie; arrayFirst ezutan egy uj neve lesz arr(1)-nek }
bind var arrayFirst to arr(1)
pervasive var ss: Stack (80) {ss az egesz programban lathato}
const iC, jC := -1
var k, l : -5 .. 5 := iC { mindket valtozo kezdoerteke iC }
bind var tombBejegyzes to a0(1) { tombBejegyzes egyszeruen a0(1) egy atnevezese }
bind readonly input to UserInput { input nem valtoztathato meg }
bind input to UserInput {itt sem}
bind (a, b, c) to d { a, b és c is d egy atnevezese }
var diskControl (at 104#8): InterruptWord := diskIdle { diskControl a 104 oktalis cimre kerul }
var p1, p2 : ^members {pointer tipus }
A láthatósági(érvényességi) kör a szövegnek egy része, melyben egy azonosító csak egyetlen jelentéssel bír. Egy érvényességi kör lehet
- eljárás vagy függvény definíció belseje,
- blokk belseje,
- rekord vagy modul definíció belseje.
A modul, eljárás és függvény érvényességi körét
zártnak nevezzük, a többit
nyíltnak. A
pervasive kulcsszó bármely deklaráció (típus, eljárás stb. is) előtt azt jelenti, hogy a deklarált azonosító minden érvényességi körben elérhető lesz. (Pervasive = mindent átható, "teljesen globális"-nak is lehetne nevezni.) Egy
pervasive-ként deklarált változó implicit konstansként importálódik minden benne lévő láthatósági körbe, ezért belül nem lehet felüldeklarálni, vagy megváltoztatni.
A rekordok vagy modulok exportált mezőazonosítói elérhetők a láthatósági körön kívül a megfelelő mezőkijelölővel.
Egy
S érvényességi körben egy olyan azonosító, melyet nem
S-ben deklaráltak, elérhető ha:
- az S-t tartalmazó érvényességi körben elérhető, ha S nyílt;
- vagy formális paramétere egy típus vagy rutin deklarációjának, amelyikben S van, vagy importálja, vagy az azonosító pervasive-nek lett deklarálva, ha S zárt.
Ennek az a következménye, hogy egy eljárásnak vagy függvénynek importálnia kell az általa használt külső azonosítókat (hacsak nem pervasive). Egy zárt érvényességi körben deklarált azonosító nem lesz elérhető azon kívül. Kivétel modulok esetén az export használata.
Ha egy
P eljárást v. függvényt importálunk az
S érvényességi körbe, akkor
S-nek minden olyan azonosítót importálnia kell, amit
P importált. (Ez csúnya és redundánsnak tűnhet ,de biztonságos. Ha
P sok külső modult, típust stb.-t használ, akkor azt mind importálni kell
S-be.)
Egy új azonosítót nem lehet bevezetni, ha már létezik egy ugyanolyan nevű, abban a körben elérhető azonosító. Természetesen egy zárt részt befoglaló részben elérhető azonosító, amit nem importáltunk, vagy nem
pervasive, nem érhető el(a zárt részen belül), ezért ilyen néven új változót lehet definiálni. Ez az egyetlen eset, mikor egy változót nem lehet elérni egy belső részben. Ha egy azonosítót használunk egy láthatósági körben, de ott nem deklaráljuk, akkor azt ott
szabadnak nevezzük.
A nyelv garantálja, hogy ugyanabban az érvnyessgi körben két azonosító nem jelölheti ugyanazt, vagy egymást átfedő változókat. (Ez fontos és pozitív tulajdonság. A fordító nyilván ellenőrzi az importlistát ‚s az aktuális paramétereket. Ezzel elkerülhetjük azokat az ismert elrémisztő eseteket, melyekben ugyanazt a változót változtatja meg egy program globális változóként és aktuális paraméterként is, "misztikus" program-viselkedést okozva.)A nyelv hatásköri szabályai kicsit bonyolultra sikeredtek, de jól kiküszöbölik a globális változók használata által okozott gondokat, megbízhatóbbá teszik a nyelvet.
Egy azonosítót hozzá lehet kapcsolni(binding) egy változóhoz, ha
var formális paraméter egy eljárás deklarációban, vagy pedig egy változó deklarációjában változó összekapcsolásban szerepel. Egy változót, amihez egy azonosítót kapcsolunk,
átnevezettnek hívunk. Az összekapcsolás láthatósági köre megegyezik a deklarációjáéval, és benne az azonosító reprezentálja a változót. Ekkor az azonosító kezdőértéke az átnevezett változó értéke lesz az összekapcsolás alatt, és az azonosítóhoz rendelt utolsó érték lesz az átnevezett változó értéke, miután a vezérlő elhagyja ezt a láthatósági részt.
Nem lehet átnevezni egy
packed struktúra komponensét, vagy egy gépfüggő rekordot. Az importált változókat átnevezettnek lehet tekinteni.
A
bind kulcsszóval deklarált azonosító (binding = kötés) nem hoz létre új változót, hanem egy már meglévőre definiál egy új azonosítót. Ha
readonly-t adunk meg utána, akkor az új azonosítóhoz rendelt értéket nem lehet megváltoztatni az adott érvényességi körben. Nem lehet neki értéket adni, illetve mint változó paramétert átadni sem őt, sem komponeneseit. Ez az alapértelmezett viselkedés is.
Végül lássunk egy EUCLID programot, amely egy számokat tartalmazó tábla típust valósít meg. (A teljes programkód megtekinthető a
Példaprogramok fejezetben (Lineáris hashelés-nél).) Egy számokból álló tömböt hoz létre, amelybe lehet beszúrni, törölni belőle, és keresni. A CyclicScan modulnak van három művelete, amivel majd az igazi keresést ( Search ) valósítjuk meg. Kezdetben a táblát inicializáljuk, üres értékekkel.
type NumberTable = module exports( Search, Delete, Insert )
pervasive const tableSize :=763; { Ez a tabla merete }
pervasive type TableIndex = 1..tableSize
pervasive type CyclicScan ( item : signedInt ) =
module exports ( Next, value, stop )
const start := (item mod tableSize)+1
var value : TableIndex := start
var stop : Boolean := False
procedure Next =
imports ( var value, start, var stop )
begin
if value = tableSize then value := 1
else value := value+1
end if
stop := (value not= start)
end Next
end CyclicScan
type State = (fresh, full, deleted)
type TableEntry( flag :State) =
record
case flag of
full => var key :signedInt end full
otherwise =>
end case
end TableEntry
var table :array TableIndex of TableEntry(any)
function Search( key :signedInt) returns Boolean =
...
end Search
procedure Delete( key :signedInt) =
...
end Delete
procedure Insert( key :signedInt) =
...
end Insert
const freshEntry : TableEntry(fresh) := ()
initially
begin
for i in table.IndexType loop
table(i) := freshEntry
end loop
end
end NumberTable
Típuskompatibilitás
A típuskompatibilitás alapelve az, hogy az azonosító a definició lerövidítése. Két típus általában akkor azonos, ha a definiciójuk egyformán néz ki. Ennek ellenére egy modultípus, vagy bármely modulból exportált típus egyedi. Definíció szerint két típus akkor azonos, ha a kiterjesztett definiciójuk megegyezik. Ezt a következő algoritmus adja meg:
Induljunk ki egy típusból. Minden típusazonosítót cseréljünk ki a definiciójára, kivéve ha az modultípus, illetve az azonosító egy modulból lett exportálva. A csere alatt helyettesítsük be az összes aktuális paramétert formálisra. Cseréljük ki minden x-re az x.itsType-ot az x típusára. Ha x formális paraméter, és a típusában előfordul a paramter szimbólum, cseréljük azt ki a további formális paraméterekkel. Ismételjük ezeket, amíg van új eredményük. Ha nincs, megkaptuk a kiterjesztett definiciót.
Két kiterjesztett típus egyenlő, ha a típus minden kiterjesztett paraméterét eltávolítva ugyanazt a szimbólumsorozatot kapjuk, és a kiterjesztett paraméterek páronként megegyeznek. A kiterjesztett paraméterek az aktuális paraméterek, az összes szabadon használt konstans azonosító értékével együtt. Ha a fordító nem tud dönteni, akkor a két típust azonosnak feltételezi, és érvényességi állítást generál. Ha egy változóhoz egy érték, illetve egy azonosítóhoz egy változó van kötve, a típusuknak azonosnak kell lenni figyelembe véve a következő szabályokat: az összes művelet operandusainak nem paraméteres típusú paramétereiknek kell lenniük, kivéve a pont, az komponens kijelölő(.,()), és a felnyíl(^) operátort.
Egy típusazonosító átlátszatlan, ha modultípus, vagy modulból lett exportálva. A modultípus mindig átlátszatlan, ezért két különböző moduldefinició mindig két különböző típust definiál, még ha szövegükben azonosak is.
Az értékadás jobb- és baloldalán a típusoknak meg kell egyezniük, kivéve:
- ha ugyanazon típus részintervallumai, ilyenkor futási időben ellenőrzi, hogy helyes-e az értékadás;
- ha a bal oldali típus T(any), a jobboldali pedig T(x) valamilyen x értékre. Fordítva ez nem igaz.
Ha egy változót "hozzákapcsolunk"(binding) egy azonosítóhoz, akkor a típusuknak meg kell egyezni.
A nyelvben van lehetőség explicit típus-konverziókra. Erre használatos a '<<=' operátor. A két típus mérete meg kell hogy egyezzen; és nem lehetnek paraméteresek. Konvertálni lehet
alprogramokat is.