Vala I. - poprvé, ale ne naposled

Kolik jazyků znáš, tolikrát si programátorem. Tak by se dalo parafrázovat známé přísloví. Konec konců, to je dnes možná jediný důvod učit se další programovací jazyk, nebo ne ? V tomto a dalších článcích se Vám pokusím představit programovací jazyk Vala a s ním spojené grafické rozhraní, tzv. toolkit GTK+.

2.6.2013 14:00 | Ondřej Tůma | přečteno 8128×

Bytecode, Prekompilace nebo Kompilace ?

Ještě než se dostaneme k samotnému programování a programovacímu jazyku, je třeba si hned na začátku vysvětlit jak to celé funguje. Na rozdíl od Javy, C# nebo Pythonu, Vala nepoužívá runtime prostředí. Překladač valac (což je zatím jediný překladač jazyka), ve skutečnosti generuje kód jazyka C a ten je pak normálním způsobem kompilován do nativní binární podoby. Tento proces se dá udělat najednou, nebo postupně. Obě možnosti si ukážeme později.

Výhoda takového jazyka by měla být celkem zřejmá. Na jednu stranu podobně jako třeba v C# má programátor přece jen trochu méně svázané ruce, má více možností jak se snadněji než v C++ dobrat výsledku, ale výsledný proces spuštění aplikace není zpomalen dalším prostředím. To platí o kompletní náročnosti na systémové prostředky výsledné aplikace.

Typy netypy

Ano, Vala je objektový programovací jazyk, inspirovaný jazykem C#, tomu také odpovídá syntax a přístup k proměným. A i když téměř ke všem typům je možné přistupovat jako k objektu, neznamená to, že opravdu objekty jsou. Vala rozlišuje typy hodnotové (čísla, výčty, znaky, text, pole a struktury), referencované (objekty) a zvláštním typem jsou delegáty (funkce) a výjimky.

Práce s těmito typy je v celku snadná, a C++kař s ní nebude mít žádný větší problém. Typy přenášené hodnotou se implicitně předávají hodnotou. Objekty a funkce se předávají referencí. Toto základní rozdělení má celkem smysl.

Konstantou mohou být jen základní hodnotové typy, tedy čísla, znaky a texty. Ve skutečnosti jsou konstanty do jazyka C transformovány jako #define. Tento přístup může přinášet pár neduhů, ale dá se to přežít a samozřejmě i využít. S textem se pak interně pracuje jako s polem znaků. Vala umí i žádný typ (void), ten je používán především v definici funkcí, resp. metod.

Funkce, přesněji metody mohou obsahovat žádný nebo N vstupních nebo výstupních parametrů a mohou vracet žádný nebo jeden parametr. Žádný (void) vstup nelze explicitně jmenovat, žádný výstupní parametr je definován jako void.


int nic_nepotrebuju(){
	return 1;
}
void testovaci_metoda(int a, bool b, double d, string s){
	// nejaky kod
}

K některým dalším specifikám, se ještě dostaneme, zde chci zmínit podle mě jednu podstatnou vlastnost jazyka Vala proti C++ a tou je absence definice const v definici metody. Stejně jako v Pythonu, objekty, které vstupují do funkcí, nemusí ale mohou být vyměnitelné. Neexistuje však způsob, jak zakázat jejich modifikaci. Toto se týká i struktur, tam je jen situace jiná ve způsobu předáváním metodě.

Objekty, Třídy, Instance, Rozhraní, a nebo ne ?

Jak už bylo napsáno, Vala je jazyk objektový, a tak se většinou programátor setká pouze s objekty a jejich metodami. Dokonce i základní funkce main, jak ji známe z C nebo C++ je metoda objektu. Objekty, resp. třídy pak můžeme rozdělit v podstatě na 3 typy: struktura, třída a třída poděděná z GObject.

Struktura je definovaná slovem struct a v podstatě se jedná o staticky alokovaný objekt. Má vlastnosti, může mít privátní nebo veřejné metody a stejně jako jiné objekty může nabývat hodnoty null. Na rozdíl od opravdových objektů je ale implicitně předávána hodnotou, tedy kopíruje se. Při inicializaci lze definovat žádné, některé nebo všechny její vlastnosti zvláštní syntaxí.

Objektový přístup k základním typům je vytvořen právě strukturou. Takže například znak (char) je definovaný jako struktura a tudíž může mít všechny ty krásné metody, ale stále je to základní datový typ, který je přenášen hodnotou.

Základní třída je podobně jako v pythonu o něco chudší. Třídy lze dědit a to i z rozhraní. Dědičnost je omezena na jednu třídu, rozhraní může být více. Vlastnosti třídy mohou být veřejné, privátní, chráněné nebo interní a lze jim definovat setter, getter a default oprávnění a operace. Díky tomu je snadné vytvořit vlastnost pouze pro čtení, ať už trikem přes jinou virtuální vlastnost, nebo prostě zakázáním zápisu.

Třídy, které dědí z GObject mají v C souboru o něco chudší definici, to proto že dědí z GObject. Díky tomu však získávají různé vlastnosti a metody od svého rodiče. Jednou takovou vlastností je signál notify, volaný vždy, když je nastavená vlastnost s příznakem set. Jak takovou vlastnost vytvořit si ukážeme později.

Signály a výjimky.

Vala, resp. knihovna GLib, jak sem již prozradil, obsahuje tzv. signály. Signály jsou zvláštní datové typy. Jsou definované jako funkce, ale dál se s nimi pracuje jako s objekty. Navěšení na signál je provedeno metodou connect a vyvolání signálu je prosté zavolání signálu jako funkce. Signály jsou řízené knihovnou GLib a celý grafický toolkit GTK+ je na nich postaven.

Výjimky se ve Vale chovají podobně jako v jiných jazycích. Výbornou zvláštností je ale nucená deklarace u metod, které mohou skončit výjimkou. V C++ lze vyjmenovat u metody výjimky, které mohou v metodě nastat. Ve Vale je toto uvedení povinné (neuvedení popravdě končí varováním). A pouze vyjmenované typy výjimek mohou v metodě nastat. Díky tomu je hned zřejmé, zda je programátor musí ošetřit, minimálně tím, že je deklaruje u své metody.

Další zvláštností je, že výjimka je vlastně zvláštním výčtovým typem. Tento typ se defïnuje klíčovým slovem errordomain, a jednotlivé výčty jsou pak chyby v dané error doméně. Vytvoření se pak provádí jako vytvoření objektu a v konstruktoru je uveden text chyby. Až si budeme ukazovat práci s výjimkami, budeme používat slova throw, try a catch.

Funkce a metody

Funkce a metody by se dali hodit na jednu hromadu, protože objekty mají metody, a chování je stejné. Je ale třeba mít na paměti, že funkce nebo statické metody mají interně méně parametrů, než metody objektů nebo struktur. To proto, že nemusí znát ukazatel na svou instanci. Ten sice programátor nevidí, ale je tam. Ve výsledném C kódu je kompilátorem dáván na poslední místo a uvnitř metod je ve Vale přístupný pod názvem this a je to reference. Podobně jako v C++ může a nemusí být při práci s vlastnostmi objektu uveden. Proč nám tento skrytý poslední parametr může zamotat hlavu, si ukážeme v některém pozdějším díle při práci s GTK+.

Metody mohou být statické, veřejné či privátní. Jedena třída nebo struktura může mít více konstruktorů, nesmí se ale jmenovat stejně. V jazyku Vala není dovoleno metody, zejména pak konstruktory přetěžovat. Ovšem řešení existuje, stačí za název konstruktoru (konstruktor má stejné jméno jako třída) přidat tečku a další text. Destruktor může být samozřejmě jen jeden a je uvozen tildou (znak ~). Práci s konstruktory si ukážeme později, tady jen malá ukázka toho jak to může vypadat:


public class Point: Object {
	public double x;
	public double y;

	public Point(double x, double y) {
		this.x = x;
		this.y = y;
	}

	public Point.empty(){
		this(0, 0);
	}

	~ Point(){
		stdout.printf("Zavolan destruktor instance Point\n");
	}
}

Existuje ještě jiný způsob zápisu konstruktoru, ten využívá klíčového slova construct. Toto slovo se uvádí nejprve u vlastnosti, a pak označuje blok kódu, který je proveden při vytváření instance objektu. Dále je pak využíváno názvu rodiče, podobně jako v Pythonu. Neboť je tento způsob atypický, nebudeme ho používat. Zájemce o bližší setkání odkáži na označení GObject-Style, z něhož vyplává, že jde o syntax postavenou právě pro GObject.

K metodám neodmyslitelně patří i jejich metody. A protože Vala obsahuje typovou kontrolu, jak již bylo poznat z předchozího příkladu, musí se typy uvádět před každou proměnou. Protože parametrem funkce je nejčastěji objekt, nebo struktura (což jsou vlastně všechny základní typy), mohl by programátor na vstupu kontrolovat, validitu takového objektu. To ale Vala dělá za něj, a pokud některý z parametrů bude roven null, program skončí assert chybou nebo dokonce nejde zkompilovat. Proto pokud programátor potřebuje vytvořit referenci na objekt, která může být rovna null, používá otazník (na stejném místě co se v C používá hvězdička pro identifikaci pointeru).

Další důležitou možností v předávání parametrů metodám jsou klíčová slova, udávající zda je předána reference, či zda jde o výstupní parametr. Pro zatím nám stačí, že lze používat slova ref (reference) a out (výstup). Slova se uvádějí při volání metody i při její definici, přičemž pokaždé v malinko jiném významu.

Specialitky

První specialitkou je dynamické přetypování a použití klíčového slova var. Znalci C++11 jistě znají auto. Slovo var dělá totéž. Díky tomu není třeba definovat typ proměnné, ten je převzat z návratové hodnoty přiřazovacího výrazu. Tedy něco jako:

var obj = new Obj();

K tomuto způsobu práce neodmyslitelně patří dynamické přetypovávání, které se hodí zejména při práci v prostředí GTK+. Metody obsluhující nejrůznější události dostávají parametr typu Gtk.Widget, případně podobný. V těle ale často programátor potřebuje získat objekt potomka. Přetypovávání se provádí například takto:

var button = widget as Gtk.Button;

Programátoři pythonu pak zajisté ocení iterátory. Jde o specifické třídy, často jde o šablony, které dědí nějaké to rozhraní, díky kterým lze použít foreach zápis. To se pak provádí na různých kolekcích, kterých je ve Vale mnoho a jde zejména o rozšíření z knihovny gir. Ta obsahuje mapu, pole i jiné další užitečnosti. Nutno podotknout že pole v základní podobě, tak jak jsem jej zmínil v sekci o typech, je vlastně totéž, jako pole v C. Šablony jdou samozřejmě využít i pro jiné věci než jen pro kolekce, ale právě ty jsou pro ně typické.

Pro python je také typické použití lambdy. Vala má lambdu také, nepoužívá však klíčové slovo, ale zápis je vypadá trochu jako v JavasSriptu. Lambda se nejčastěji používá pro jednoduché funkcionality, například logování, nebo sort algoritmy.


this.signal.connect((widget, data) => {
		stdout.printf("%s\n", data.to_string());
	});

Poslední specialitkou kterou zmíním je dynamická práce se stringy. Tu znají především programátoři interpretovaných jazyků. Celý vtip je v tom, že místo nahrazování za formátovací znaky, což umí některé funkce (např. printf), nebo lepení stringů, se použije zastoupení přímo ve stringu. Zápis vypadá takto:


string text = "Textová proměnná";
int cislo = 123;

stdout.printf(@"Text: $text a číslo: $cislo\n");

Závěrem

Sem si plně vědom že tento úvodní článek byl spíše výčtem vlastností jazyka Vala, než jeho názornější ukázkou. Stejně tak sem si plně vědom, že jsem mnoho vlastností nezmínil a zatajil, nebo alespoň hodně zjednodušil. Všechny tyto nedostatky se pokusím napravit v dalších článcích. Na příště se můžete těšit na trochu praktiky, vytvoříme si hello world aplikaci. Ukážeme si nějaké konkrétní typy a základní možnosti jazyka. Podíváme se na konvenci při psaní kódu a probereme jeho kompilaci. Všem také doporučuji nahlédnout do tutoriálu na stránkách projektu, nebo alespoň do české wikipedie.

Zdroje na webu:

https://live.gnome.org/Vala/
https://live.gnome.org/Vala/Tutorial
http://cs.wikipedia.org/wiki/Vala_(programovací_jazyk)

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1977