21CTO导读:Virgil 体现了面向“轻量级高性能系统”的语言趋势,该语言具有强大的交叉编译器。我们对其进行了测试。
我最近看到Virgil语言的作者 Ben Titzer “发表”了一句话,他也是 WebAssembly 的共同创建者,他这样说道:
“我并不想与 Zig 竞争,但最近我把玩了一下C 语言的竞争对手 Zig,我很想知道哪种语言不会与它竞争”。
Titzer 还说 Rust“无法做到 Virgil 能做到的事情”。
Virgil 的目标是什么?
Titzer 在谈及新语言的目标说:
“我希望让 Virgil 成为一种出色的系统编程语言,它能去除遗留的垃圾,同时还具有编写健壮系统代码的强大功能。例如虚拟机、编译器、内核、网络堆栈等。”
除了 Rust,还有一种趋势是新语言瞄准“轻量级高性能系统”,并配备强大的交叉编译器。。
构建高性能系统背后的设计推理之一是编译器优化、代码树、展开分支、保护条件、安全性等复杂的组合。其中许多会随着时间的推移而变化,例如,今天内存很便宜,但安全性同样重要,但 30 年前情况恰恰相反。
在这篇文章中,我将重点介绍如何使用Virgil语言。我们将看看它与Zig和轻量级趋势相比如何。Virgil 有类和函数,甚至有类型参数,所以它给人的感觉也是很现代。
开始使用 Virgil
脱离上下文来看,要求选项有点不寻常:
所以这一次,我的旧款 Macbook 又受到了本人的欢迎。
启动基于 Rust 的终端 Warp后,您只需克隆存储库即可拿到virgil。
如果您有一段时间没用过 GitHub,那么在存储库首页的代码选项卡下会为你显示克隆行:
因此,现在我们只需在 Shell 中做如下克隆命令:
现在,我可以从任何地方调用 Virgil。考虑到它的 bin 目录位置,我将添加到了系统路径:
export PATH=$PATH:~/Projects/TheNewStack/virgil/virgil/bin
这样,便搞定了!让我们再编写无法避免的第一个 Virgil 程序:
def main() {
System.puts("Hello World!\n");
}
我们使用内置解释器运行它,如下图所示:
是不是很好?
但是,如果我们想编译它,我们需要针对主机和硬件架构。幸运的是,如果我们想不同主机上运行,Virgil 会做简单地检测:
请大家注意,现在目录中有一个可执行文件。(虽然 Warp 向我们显示了时间,但请观察到我的机器是一台 2015 年 Macbook Pro ,它的年龄和状态,以免影响你的执行速度)
运行编译后的代码得到:
虽然后续运行速度要快得多,但我们显然没有做足够的工作来考虑时间等问题。
上面我在本机平台上运行,否则,Virgil 会给我们一个 JAR。为了测试交叉编译器,让我们强制它快速生成一个 JAR:
还有以下…
因此,我们可以将其与 Zig 中的交叉编译功能进行比较。
让我们做到更多,比如得到可变类型var:
// 'var' introduces a new, mutable variable
var x: int = 0; // with type and initializer
var y: int; // with type, initialized to default
var z = 0; // with initializer and inferred type
不可变变量的引入def方式完全相同。这是一个接受整数并返回整数的方法:
// recursive computation of fibonacci sequence
def fib(i: int) -> int {
if (i <= 1) return 1; // base case
return fib(i - 1) + fib(i - 2); // recursive calls
}
def main() {
System.puti(fib(6));
}
上面的代码打印出了整数 13,这是应该的结果。我们可以将其放入一个类中,并将其保存为fib.v3:
class Fib {
def min: int = 1;
def calc(i: int) -> int {
if (i <= min) return min; // base case
return calc(i - 1) + calc(i - 2); // recursive calls
}
}
def main() {
var obj = Fib.new();
var z = obj.calc(6); // method call
System.puti(z);
System.puts("\n");
}
代数数据类型
Virgil 特有的代数数据类型。这些似乎是多态性、开关和枚举的有趣组合,用于创建复杂的结构,而无需使用对象。
举个例子,以下是一个Travel类:
type Travel {
case Walk {
def distance(hoursTravelled: int) -> int {
return hoursTravelled * 3;
}
}
case Cycle {
def distance(hoursTravelled: int) -> int {
return hoursTravelled * 16;
}
}
case Drive (speed: int) {
def distance(hoursTravelled: int) -> int {
return speed * hoursTravelled;
}
}
def distance(hoursTravelled: int) -> int; // top-level method declaration
}
def main() {
var walking = Travel.Walk;
var busTrip = Travel.Drive(50);
var cycling = Travel.Cycle;
var totalDistance = walking.distance(1) + busTrip.distance(2) + cycling.distance(1);
System.puts("Today, We covered a distance of ");
System.puti(totalDistance);
System.puts(" miles in 4 hours.n");
}
// Today, We covered a distance of 119 miles in 4 hours.
该类型是一种不可变结构,附带一个顶级方法。它们可以进行深度比较以确定是否相等;case 和参数必须匹配。在其他方面,这些case值也像枚举类型 (enums) 一样。
Virgil 也有指针
在主机目标代码上,Virgil 确实也为开发者提供了指针,但这些指针本质上也是不安全的。
这是故意这样设计的,因为语言必须在字节级别拥有完全控制权。毕竟,我们被提醒“不仅整个编译器,而且整个运行时系统和垃圾收集器都是用 Virgil 编写!”
指针被实现为无类型的原始字节地址。它们的工作方式与 C 语言类似。它们的大小与目标类型相同,您可以对它们进行加减运算、比较等。
以下的指令load和store用于以“危险”的方式访问内存:
var p: Pointer;
var x: int = p.load(); // load an int (i32) from {p}
p.store(33); // store an int into {p}
var y: string = p.load(); // unchecked, raw reference load, dangerous!
这显然意味着开发者与主机、操作之间的关系与其它高级语言并不一样。
各位肯定知道一句话,“如果你长时间凝视深渊,深渊也会凝视你”,但还会导致无法追踪的错误。
结语
虽然 Virgil 语言还很年轻,但它已经具有很好的实用性了。
我没有介绍所有的功能,因为这篇小文只是一个甜点。但目前显然它是一个很活跃的项目。
您可以以面向对象的方式编码,也可以用函数式的方式编码。这有时被可爱的人们称为多范式语言,但最终,这是另一个强大且轻量级的项目,可以保持低级开发的健康。
作者:万能的大雄
参考网址:
https://github.com/titzer/virgil
https://github.com/titzer/virgil/blob/master/doc/tutorial/ADTs.md