Vala

Vala 是一門面向物件的程式語言，由自舉編譯器產生 C 語言程式碼和使用 GObject 系統，允許在 GNOME 執行時庫的基礎上使用大量現代的程式設計技巧。透過使用 GLib 和 GObject，Vala 提供了動態型別系統和基於引用計數（reference counting）系統的記憶體管理的功能。

描述

Vala 是一門程式語言，結合了指令碼語言的高階程式語言建造時間效能，與低階的程式語言執行時間效能。相較於用 C 寫成的應用和庫，它致力於將現代程式語言特徵帶給 GNOME 開發者，而不用強加任何額外的執行時間要求，並且不用使用不同的 ABI。Vala 的語法類似於 C#，併為了更好的適應 GObject 型別系統而做了修改。

Vala 由 Jürg Billeter 構想，並由他和 Raffaele Sandrini 實現，他希望開發 GNOME 應用能有對 C 語言的更高階替代者。他們的確喜歡 C# 的語法和語義，但不想使用 Mono，故而他們在 2006 年 5 月完成了一個編譯器。最初它使用 C 來，一年後（在 2007 年 7 月的版本 0.1.0 發行中），Vala 編譯器實現了自舉。

版本歷史

語言設計

3.1 特徵

Vala 使用 GLib 和它的子模組（GObject、GModule、GThread、GIO）作為核心庫，它們支援多數作業系統，並提供：平臺無關執行緒、輸入 / 輸出、檔案管理、網路套接字、外掛、正則表示式等。Vala 的語法目前支援如下現代語言特徵：

介面
屬性
Foreach
lambda 表示式
區域性變數的型別推論
泛型
輔助記憶體管理
異常處理

圖形使用者介面可以使用 GTK 部件工具箱和 Glade 來開發。

3.2 記憶體管理

對於記憶體管理，GType 或 GObject 系統提供引用計數。在 C 語言中，程式設計者必須手工管理增加和移除引用，而在 Vala 中，管理這種引用計數是自動化的，如果程式設計者使用語言內建引用型別而非普通指標的話。唯一需要當心的是避免生成引用環，因為在這種情況下記憶體管理系統不能正確工作，此時需要使用 Vala 自帶的 week 關鍵字標註弱引用解決。

對於不支援引用計數的型別，Vala 也支援按照生命週期自動釋放記憶體的非引用計數記憶體管理；Vala 還可以透過指標手工記憶體管理。

3.3 繫結

Vala 意圖提供但現存 C 庫的執行時間訪問，特別是基於 GObject 的庫，而不需要執行時繫結。要透過 Vala 使用一個由 C 語言編寫的庫，全部所需就是一個 API 檔案（.vapi），包含採用 Vala 語法的類和方法宣告。但是不支援 C++ 庫。目前在 Vala 的每次發行中，都包括了 GTK 以及大部份的 GNU 專案和 GNOME 平臺的 vapi 檔案。還有用 Vala 寫的一個庫叫做 Gee，為常用資料結構提供基於 GObject 的介面和類。

為從用其他語言比如 C# 寫的應用訪問 Vala 庫，寫繫結生成器也應當是容易的，因為 Vala 解析器被寫為一個庫，所以所有執行時資訊在生成一個繫結的時候都是可獲得到的。

工具

4.1 編輯器

Vala 的開發工具近些年來有了顯著改進。下面是具有支援 Vala 程式設計的外掛的一些流行 IDE 和文字編輯器：

GNOME Builder
Visual Studio Code，具有 Vala 外掛
Vim，具有 arrufat/vala.vim 外掛
Emacs，具有 vala-mode
Atom
Geany
Kate

4.2 程式碼智慧

目前，有兩個活躍開發的語言伺服器為 Vala 提供了程式碼智慧：

，設計用於支援 LSP 的任何編輯器，包括 VSCode、vim 和 GNOME Builder。
，目前是 Vala 在 GNOME Builder 中的預設語言伺服器，它對支援 LSP 的任何編輯器提供支援。

4.3 建造系統

目前，有一些建造系統支援 Vala，包括 Automake、CMake、Meson 等。

4.4 除錯

對 Vala 程式的除錯可以使用 GDB 或。對於 IDE：

GNOME Builder 擁有透過 GDB 的對 Vala 的內建除錯支援。
Visual Studio Code 擁有對 GDB 和的擴充套件，比如 cpptools 和 CodeLLDB。

示範程式碼

一個簡單的 “Hello world” 程式：

void main {print ("Hello World\n");

}

5.1 面向物件

下面是更復雜的一個版本，表現了 Vala 的一些面向物件的特徵：

class HelloWorld: Object {

	private uint year = 0;

	public HelloWorld

	public HelloWorld.with_year (int year) {if (year> 0)

			this.year = year;

	}

	public void greeting {if (year == 0)

			print ("Hello World\n");

		else

			/ Strings prefixed with '@' are string templates. /

			print (@"Hello World, $(this.year)\n");

	}

}

void main (string args) {var helloworld = new HelloWorld.with_year (2021);

	helloworld.greeting ;}

因為在 GObject 庫的情況下，Vala 不支援多重繼承，但是 Vala 中的類可以實現任何數量的介面，它可以包含它們的方法的預設實現。下面一段程式碼展示具有預設實現的 Vala 介面（有時稱為 mixin）：

using GLib;

interface Printable {public abstract string print ;

	public virtual string pretty_print {return "Please" + print ;

	}

}

class NormalPrint: Object, Printable {string print {return "don't forget about me";}

}

class OverridePrint: Object, Printable {string print {return "Mind the gap";}

	public override string pretty_print {return "Override";}

}

void main (string args) {var normal = new NormalPrint ;

	var overridden = new OverridePrint ;

	print (normal.pretty_print );

	print (overridden.pretty_print );

}

5.2 訊號和回撥

下面是基本例子，展示如何定義一個訊號於非緊湊（compact）的一個類中，它擁有用 Vala 透過 GLib 建造的一個訊號系統。接著註冊回撥函式到這個類的一個例項的這個訊號。這個例項可以傳送這個訊號，而連線到這個例項的每個回撥函式（也叫做處理器），將安它們連線上的次序來呼叫：

class Foo {public signal void some_event ; // 定義一個訊號

public void method {some_event ; // 傳送這個訊號（回撥得以呼叫）

}

}

void callback_a {print ("Callback A\n");

}

void callback_b {print ("Callback B\n");

}

void main {var foo = new Foo ;

foo.someevent.connect (callbacka); // 連接回調函式

foo.someevent.connect (callbackb);

foo.method ;}

5.3 執行緒

在 Vala 中新執行緒是程式碼的一部份，比如要求在執行時間併發執行的一個函式。新執行緒的建立和同步是用時 GLib 中的 Thread 類完成的，它在建立新執行緒時接受一個函式作為引數，如下面（非常簡化的）例子所示：

int question {

// 一些 print 操作

for (var i = 0; i ("question", question);

print (@"$(thread.join )\n");

}

5.4 圖形使用者介面

使用 GTK 來建立一個圖形使用者介面的 "Hello, World!" 程式：

int main (string args) {

var app = new Gtk.Application (

"com.example.App",

ApplicationFlags.DEFAULT_FLAGS

);

app.activate.connect ( => {var win = new Gtk.ApplicationWindow (app) {

title = "Hello, World!",

default_width = 350,

default_height = 70

};

var label = new Gtk.Label ("Hello, World!");

win.child = label;

win.present ;});

return app.run (args);

}

在 GTK 4 平臺上，需要以下命令來編譯這一示例:

valac --pkg gtk4 hellogtk.vala