A Nemerle programozási nyelv

Típusok, típuskonstrukciók



Elemi típusok

A Nemerle típusrendszere alapvetően a .Net framework típusrendszerére épül. Ebben az alapvető primitív típusok a következők:

Név.Net típusLeírás
byteSystem.ByteElőjel nélküli 8 bites egész szám.
charSystem.CharUnicode karakter.
boolSystem.BooleanBoolean típus: vagy true vagy false
sbyteSystem.SbyteElőjeles 8 bites egész szám.
shortSystem.Int16Előjeles 16 bites egész szám.
ushortSystem.Uint16Előjel nélküli 16 bites egész szám.
intSystem.Int32Előjeles 32 bites egész szám
uintSystem.Uint32Előjel nélküli 32 bites egész szám.
floatSystem.SingleEgyszeres pontosságú lebegőpontos szám.
doubleSystem.DoubleKétszeres pontosságú lebegőpontos szám.
decimalSystem.DecimalFixpontos szám 28 számjegyig, tárolja a tizedespont helyét. Pénzügyi számolásokban használatos.
longSystem.Int64Előjeles 64 bites egész szám.
ulongSystem.Uint64Előjel nélküli 64 bites egész szám.
objectSystem.ObjectMinden adattípus ebből az típusból származik. Referencia típus.
stringSystem.StringA sztring típus Unicode karakterek sorozatát jelöli.

A Nemerle saját típusai

A Nemerle új típusokat is bevezet a meglévőkön kívül.

Tuple típus

A tuple a direkszorzat-típus módszertanhoz legközelebb álló megvalósítása. Névtelen struktúra, melyekben az elemeket a pozíciójuk szerint azonosítjuk. Akkor szokás (és lehet) használni, ha egy függvény több értéket is visszaad, de eleslegesnek tartjuk új struktúra bevezetését programunkba. A tuple jelölése: típus * típus { * típus ... }. Konstrukciós művelete: (kif1,kif2,kif3..), dekonstrukciós művelete: def (x,y,z...) = kifejezés. A következő példa frappáns bemutatása a tuple típusnak (egyben a típuskövetkeztetésnek, és a funkcionális függvényeknek):

def swap(x, y) { (y, x) }; mutable x = 5, y = 6; (x, y) = swap(x, y); // x=6, y=5 az eredmény

Másik példa, immár teljes típusjelöléssel:

def osztas(a : int, b : int) : int * int { (a / b, a % b); } mutable int a=12, b=5; (a, b) = osztas(a, b);

Lista típus

A lista típus (list) a Nemerle egyik speciális típusa, olyan listákhoz érdemes alkalmazni, amiket egyszer hozunk létre, nem változtatunk, viszont sokszor használunk. Emellett persze használható a .Net List típusa is.

Szintaxis: [ elem1, elem2, elem3, ... ]

Függvény típus

A Nemerlében léteznek delegate-ek, mint a C#-ban, de csak a .Net által megkövetelt helyen érdemes használni őket, helyette inkább a függvénytípusokat részesítik előnyben. Ezek anonim típusok, szintaxisuk a következő: függvényváltozónév : típus * típus * ... -> típus * típus * ... A függvény paramétereit és a visszatérési értékét a tuple típus mintájára definiáljuk. A következő Nemerle program bemutatja a funkcionális értékek és változók használatát:

using System; module Program { public static mutable f : void -> string; static Save(g: void -> string) : void { f = g; } static Call(h: string -> void) : void { h(f()); } static Main() : int { if (false) { def f() { "hello"; }; Save(f); } else { def f() { "bello"; }; Save(f); } Call(System.Console.WriteLine); // a kimenet: bello } }

Variant típus

Az altípusosság és polimorfizmus könnyű kezelésére vezették be a Variant típust a Nemerlében. Legegyszerűbb példája a C# enum típushoz hasonlít(természetesen Nemerlében külön van enum típus, ami megegyezik a C# ilyen típusával ezért eme leírás nem tárgyalja külön), komolyabb használati esetei viszont a C union típusához, illetve az altípusosság kezeléséhez kellhetnek.

Egyszerű példa:

variant Color{ | Red | Yellow | Green }

Ez esetben szinte semmi nem különbözteti meg az enum típustól, legfeljebb a használatának szintaxisa.(az enum-nak ugyanez a szintaxisa, csak a variant kucsszó helyett enum található). Az enumtól való különbözőséget a következő példa szemlélteti:

variant RgbColor { | Red | Yellow | Green | Different { red : float; green : float; blue : float; } } def _blue = RgbColor.Different(0f, 0f, 1f); def _red = RgbColor.Red();

A Different altípusnál lehet paramétereket is megadni, ez esetben a Nemerle fordító automatikusan létrehoz egy kontruktor-t. A Variant típus erősségét leginkább a match vezérlési szerkezettel együtt lehet kihasználni, ami (távolról) hasonlít a C# switch szerkezetére, de annál lényegesebben több lehetőség rejtőzik benne. Az alábbi példa ezt szemlélteti, bináris keresőfákon keresztül.

variant Tree { | Node { left : Tree; elem : int; right : Tree; } | Null } // beilleszti az e elemet a t fába, és visszadja a fát Insert(t : Tree, e : int) : Tree { match (t) { | Tree.Node(l, cur, r) => if (e < cur) Tree.Node (Insert(l, e), cur, r) else if (e > cur) Tree.Node (l, cur, Insert(r, e)) else // a csúcs már létezik a fában t | Tree.Null => Tree.Node (Tree.Null(), e, Tree.Null()) } } // ellenőrzi, hogy a megfelelő elem létezik-e a fában. Contains(t : Tree, e : int) : bool { match (t) { | Tree.Node(l, cur, r) when e < cur => Contains(l, e) | Tree.Node (l, cur, r) when e > cur => Contains(r, e) | Tree.Node => true | Tree.Null => false } }

Látszik, hogy ez a megközelítés nagyon közel áll a módszertani megközelítéshez.

Változók, konstansok

A Nemerle alapvetően erősen típusos nyelv, de a fordító típuskövetkeztetési rendszert használ arra, hogy kitalálja a változók típusát. Ezért nem kell minnden esetben jelölni a változók típusát a deklarációjuknál. Például:

def i = 5; //int def s = "Hello"; //string def f = new Form(); //Form def j = Convert.ToInt32("12"); //int

A Nemerlében alapvetően két fajta változó étezik: mutable és immutable. A mutable változó értéke folyamatosan változhat a program futása során, és a mutable kulcsszó vezeti be. Az immutable változó csak egyszer kaphat értéket, értéke nem változik, és a def kulcsszó vezeti be.

A konstansokat a const kulcsszó vezeti be, funkciójuk ugyanaz, mint C#-ban.

Operátorok, kifejezések

Az operátorok szerepe és precedenciája megegyezik a C#-ével, pár különbséggel, például a Nemerlében bevezethetünk saját operátorokat is (nemcsak a meglévőeket terhelhetjük túl). Az x++ és ++x kifejezések értéke void, ezért egymással teljesen ekvivalensek. A Nemerlében nem létezik ? : operátor.

A nyelv nem tesz különbséget a kifejezés és az utasítás között, a Nemerlében minden kifejezés. Ezzel megkönnyítjük az értékeken való műveletvégzést, ugyanis minden kifejezés visszatér, eredménye a kifejezés értéke. Egyetlen kivétel létezik, a throw kulcsszó, ami megszakítja a program futását kivétel kiváltásával. Természetesen a {} blokk is egy kifejezés, értéke a legutolsó kiértékelt kifejezés értéke a blokkban, így nincs szükségünk return utasításra.