Az Euclid programozási nyelv

Típusok

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.:

A típusok:

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 :

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:

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:

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
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:
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 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.