Raku Features
— 焉知非鱼Raku Features
(这是同一作者的法语文章 Raku en 2020 的翻译/改进版。)
感谢 Elizabeth Mattijsen 帮助校对和提供建议。
目录 #
- About Raku
- Community
- Rakudo
- Features of the Raku language
- Implementations based on Rakudo
- Conclusion
在这两篇博文中的第一篇博文中,你会发现一个总的介绍,包括一些社区的见解和关于解释器和虚拟机(Rakudo 和 MoarVM) 的细节。
在第二部分中,你会得到一个关于 Raku 功能的长篇描述,以发现为什么 Raku 值得讨论和学习。
(下一篇博文是关于过去的实现和历史细节)
这篇博文是用爱写的 ❤️
关于 Raku #
Raku 是一种强大而成熟的编程语言,它很难实现(这将在本篇文章的第二部分介绍)。
Raku née “Perl 6” 不再是 Perl 5 的下一个版本(将是 Perl 5.36 或 Perl 7)。Raku 已经在 2019 年被重新命名,以明确这一状态。但 Raku 与 Perl 5 有很多共同之处。
-
一些技术特点(接近于自然语言,符号
-
相同的基金会
-
相同的会议(但 Raku 最近有了它的第一次专门会议)
-
部分是他们的社区(Damian Conway, Larry Wall…)
-
一些活动(Perl 每周挑战赛…)
最近,Raku 已经与 Perl 5 有了很大的不同,我们可以简单地用 “Perl” 来指定 Perl 5(或 Perl 7),而在任何情况下,我们更愿意用 “Raku” 来谈论 Perl 6。有一段时间,Perl 6 社区略微反对这种重命名,但整体意见发生了变化,重命名在 2018 年和 2019 年之间顺利完成(详情见本期的下一篇博文)。
你可以通过订阅《Raku 周刊》来关注 Raku 新闻。
Raku 有一个小的社区,但有一些"杀手级应用"。
来自 Raku 界的大人物 #
在讨论重新命名和 Raku 版本的细节之前,让我们着重介绍一些过去和现在对 Raku 的头部贡献者。
- Jonathan Worthington: Raku 的现任首席开发者(采访)。
- Elizabeth Mattijsen: 对 Raku 很有研究。
- Patrick Michaud: 前 Rakudo、NQP 等…的主要开发者。
- Audrey Tang: 台湾现任部长, 原 Pugs 的主要开发者 (Perl 6 是用 Haskell 实现的, 请看这个旧采访或这个采访
- Damian Conway: 教师、演说家、发言人和多产的开发者。
- Larry Wall: Perl(BDFL) 的创造者和 Raku 的创造者。无需多言。 2021 年,没怎么参与了。
- Moritz Lenz: 作家和头部贡献者。
- Flavio S. Glock: Perlito 的主要开发者。
并非所有的人都还在活跃,而且这个名单也并非详尽无遗。
Raku 社区最近决定设立一个指导委员会。
Raku(do) #
Raku 最先进的实现是 MoarVM(虚拟机)上的 Rakudo(解释器)与 NQP(内置)。
当 Rakudo 发布时,Rakudo + MoarVM 的捆绑被称为 Rakudo Star(或Rakudo *)⭐
自 2015 年以来,Raku 的实现是"功能完整"的,在编译器和虚拟机上的努力更多的是修复问题和提高性能。
Raku 有多个官方版本(最重要的是在 2015 年)。
-
Rakudo * 的第一个公开版本在2010年7月(主要版本,但不是功能完整的,仍然基于 Parrot → Rakudo + Parrot)
-
6.c 2015 年的"圣诞节”(公告)(Rakudo + MoarVM)
-
6.d 2019年11月的"排灯节"(几个变化的细节))(Rakudo + MoarVM)
-
6.e 将是下一个主要版本(Rakudo + MoarVM)。
这个版本划分方案从何而来?
很简单,来自一个关于 Raku 发布日期的经常性笑话。通常的回答是"会在圣诞节前准备好"😀。
除了主要版本之外,从事 Raku 工作的团队每月都会产生次要版本。
一些书籍 #
在进入语言细节之前,这里有一些书可以帮助你了解更多关于 Raku 的知识。
- Perl 6 Essentials
- Learning Perl 6
- Think Perl 6
- Perl 6 quick syntax reference
- Perl 6 deep dive
- Raku fundamentals
一些链接 #
技术细节从这里开始,我希望你准备好了 😀
Raku 的力量 #
Raku 的功能令人印象深刻,我想从这个开始 :)
Raku 源于 Larry Wall 的语言技能,因此 Raku 受自然语言的影响很大。Raku 是多范式的(过程式、函数式、并发式编程和面向对象)。
Raku 的价值观可以在 Raku 宣言中找到 (1, 2, 3)。
像它的表亲 Perl 一样。
与 Perl 5 相反。
- 与原生代码的接口非常简单。
- 面向对象的编程集成在了语言中(框架+对象类型)。
- 该语言可以处理并发问题,并为其提供了一个完整的工具箱。
- Unicode 是"默认"处理的。
对于更多的技术细节,这里有 Raku 的术语表和编译器之间的功能比较(它提供了一个很好的语言概述)。在接下来的段落中,将更详细地解释 Raku 的功能。
像 courses.raku.org 或 raku.guide 这样的课程可能是很好的 Raku 功能的"展示"。
类型 #
- 渐进式类型化(动态和静态类型化)。
# Dynamic
my Cool $var = 42;
# Functions using $var will coerce to another type
# upon their needs
# Static
my Str $var = "Raku";
- 签名中的注解:
sub ab(Int $a, Int $b) { return $a+$b; };
say ab(1,1);
- 类型推断
- 异形体(和多态性求值):
IntStr $foo
- Junctions:
my $junction = 1|2|3;
$junction += 4; # junction now equals 5|6|7
$junction += (1&2); # junction now equals (6|7|8)&(7|8|9)
- Subsets: 允许扩展同构类,可以作为约束条件,同时在运行时被解析:
subset Even where * %% 2;
say 1 ~~ Even; # False
- 强制转换:
sub i-cast(Int() $n) { say $n.^name };
i-cast "42";
约束 + 强制转换 (“我想要一个字符串对象,并将其转换为整数对象”):
sub f(Int(Str) $want-str-cast-to-int, Str() $cast-to-str) { } ;
f '10', 10;
或者条件约束(“我的参数应该是一个整数对象或一个字符串对象”)。
sub xyz($n where $_ ~~ Int | Str ) { say +$n }
- 原生类型:
my int8 $verysmall = 42;
say $verysmall;
- 自动装箱: 从原生类型到对象的自动转换。
- Auto‑vivification in the hash/arrays
my %a;
%a{'autovivification'}++;
say %a{'autovivification'};
- 例如,Twigils来定义变量的作用域。
- 对象容器的完整集合。参见复合类型列表(SetHash, Bag, Map, list, Mix, Pair, Set…)
- 成型的数组/哈希(一种矩阵或多维哈希)。
- 紧凑的数组:
my int @array
,它是以连续的方式存储的 - 惰性列表(也见
gather
)。
my @list = (1, 1, * + * ... *).grep({ $_.is-prime });
say @list[4]; # This generates elements 0..4.
# Elements 5..∞ haven't been generated yet
例程 #
- 参数:
sub plus2($foo) { return $foo+2; }
- 类型化:
sub mul3(Int $foo) { return $foo*3; }
- 带有属性 rw 或 copy:
sub make42($foo is rw) { $foo = 42; }
- 多重分派:
multi sub sayit( Int $n ) { say "Number: $n"; }
multi sub sayit( Str $s ) { say "String: $s"; }
分派是基于参数的数量、它们的类型、约束条件(我们甚至可以用函数 nextsame
、nextwith
、callsame
、callwith
来玩)。
参见"正确的窄分析"以获得最佳的候选者解析。
- 剩余参数的吞噬和参数的扁平化(像在 Perl 5 中)。
Operators 运算符 #
- Raku 有大量的运算符,请看完整的列表。
异步、并行和并发 #
一个完整的工具箱,用于处理异步、并行和并发。
- 可使用 Proc::Async 进行外部调用的承诺(或未来)。
- 用 Channel 和 Supply 处理流,以实现事件驱动或发布/订阅模式
- 对线程、调度器和锁等低级原语的访问
面向对象编程和元对象编程 #
有能力定义我们自己的操作符(见上文)和功能。元对象编程在虚拟机中被部分处理。
- 单一或多重继承。
- 自省。
- 实例化后"编辑"一个类(例子)。
- 方法的组合/封装。
- 信任关系,允许访问另一个(父)类的私有方法而不使其继承。
- 代理
has $.base is rw handles <meth1 meth2>
。 - 不管是什么类型的 “*"(这就是 “Rakudo star” 这个名字的由来)。
Grammars #
支持语言级的 grammar(grammar
, token
, rule
, regex
)!
grammar Calculator {
token TOP { <calc-op> }
proto rule calc-op {*}
rule calc-op:sym<add> { <num> '+' <num> }
rule calc-op:sym<sub> { <num> '-' <num> }
token num { \d+ }
}
class Calculations {
method TOP ($/) { make $<calc-op>.made; }
method calc-op:sym<add> ($/) { make [+] $<num>; }
method calc-op:sym<sub> ($/) { make [-] $<num>; }
}
say Calculator.parse('2 + 3', actions => Calculations).made;
接口 #
原生语言支持真的非常容易!
- 轻松实现与原生语言的对接(Native Call)。
use NativeCall;
# Interface a function from unistd.h
sub getpagesize() returns int32 is native {};
# Interface a function from a shared library (e.g. libcalculator.so)
sub add(int32, int32) returns int32 is native("calculator") {};
- 用 Inline::Perl5 与 Perl 5 交互。
use Test::More:from<Perl5>;
plan tests => 1;
use Inline::Perl5;
my $p5 = Inline::Perl5.new;
$p5.call('print', 'Hello World');
在字素层面处理 Unicode #
非常高的 Unicode 管理水平,特别是由于 MoarVM 虚拟机中的原语。
语言运算符本身可以是 UTF-8 的(比如你创建的)。
其他批量功能 #
- 尖号块(匿名闭包)。
my $cube = -> $x {$x ** 3};
say $cube(3); # 27
按字母顺序排序:
my $pow = {$^x ** $^y};
say $pow(3, 4); # 81
或者甚至:
for 0..9 {
say "$^n2, $^n1";
}
或者用命名法:
my $pow = {$:base ** $:exp};
say $pow(:base(25), :exp(2)); # 625
Rakudo Star(自 2010 年起)
Rakudo Star(或 “Rakudo *”) 的第一个主要版本是基于 Parrot(PCT + ParrotVM),但我们将忽略这个版本,而专注于当前的实现(Rakudo + MoarVM)。
编译 (Rakudo) #
Rakudo 是 Raku 的一个编译器,它实现了整个语言,可以针对几个不同的虚拟机(主要是 MoarVM)。Rakudo 的大部分内容是用"简化 Raku”(NQP)编写的。Rakudo 还实现了预编译以优化其性能。除此之外,Rakudo 还使用几种优化方法改进了语法树,下面是一些例子。
- 转换后的范围迭代是在原生循环中进行的
- 通过编译时的解析方法对私有方法进行去虚拟化
- 去除(静态的)死代码
- 匿名化一个变量
- 减少变量的作用域
- Junction 的展开
NQP #
NQP 是一个类似于 MiniPerl for Perl 5 的编译器。 它是一个引导工具,帮助编译 Rakudo 的 Raku 部分,并在运行已经编译好的库之前编译好库。与 MiniPerl for Perl 5不同(它是一个可以解释所有 Perl 语法的解释器,但缺少一些电池:指没有模块混合 Perl 代码和本地代码),NQP 只能编译一个"简化的 Raku"。NQP 既指编译器,也指以 “.nqp " 为扩展名的文件所包含的源代码。
一些 NQP 代码看起来像这样。
method symtable() {
%!symbol := nqp::hash() if nqp::isnull(%!symbol);
%!symbol
}
method evaluate_unquotes(@unquotes) {
my $result := self.shallow_clone();
my $i := 0;
my $elems := nqp::elems(@(self));
while $i < $elems {
$result[$i] := self[$i].evaluate_unquotes(@unquotes);
$i := $i + 1;
}
$result
}
NQP 是 Rakudo 的一部分,其灵感似乎来自 MiniPerl6/KindaPerl6。
Perlito(见下文)也实现了同样的"简化 Raku”。
Execution (MoarVM) #
MoarVM 是 Metamodel On A Runtime VM 的缩写(官方网站)。它是一种基于注册机的虚拟机,就像 Parrot 一样。Jonathan Worthington 是 MoarVM 的创始人和建筑师。
下面是 MoarVM 的一些特点。
- 内部表示法更接近于Raku(与Parrot相比)(“另一方面,如果虚拟机不支持,有些功能是很难有效实现的。",根据 Flavio Glock 的采访)。
- 优秀的 Unicode 支持,字符串在字素级别上表示。
- 对异步和并发编程的管理,包括并发编程的控制结构、同步套接字、定时器、进程、信号灯、阻塞队列等。
- 精确的垃圾收集,世代相传(年轻的→次老的→大桶)和管理竞争
- 能够管理连续性
- 有界的序列化,将加载模块的代码序列化,以备日后使用,并通过避免重构该代码的中间表示法来节省启动时间
- 运行时加载代码
- 优化:消除尾部调用(如果函数返回在调用中,就不要在调用栈中堆积),调度,消除死代码,栈上替换(用优化的例程替换未优化的例程栈),逃逸分析(将堆的分配转化为栈上甚至寄存器中的分配)和标量替换(消除内存的分配)
- JIT 编译
- 对调用堆栈和分配进行分析
- 堆快照:主要用于进一步扫描(例如调查和改进垃圾收集)。
我还推荐阅读这个 MoarVM 的介绍。
MAST #
MAST 是 MoarVM AST(抽象语法树)的缩写。
MBC #
MBC 是 MoarVM Byte Code 的缩写。这里是看起来像 MBC 的东西。
; Grab value out of Scalar
decont r4, r1
; Grab type ; de-Scalar if needed
wval r5, 1, 34
decont r3, r5
; Ensure arg is an Int
istype r6, r4, r3
assertparamcheck r6
基于 Rakudo + MoarVM (since 2012) 的实现 #
MoarVM 没有汇编器。
下面是另一张架构图,取自关于 Rakudo/MoarVM 优化的演讲(2014)。
结论 #
这第一篇博文更多的是处理关于 Raku 的当前信息。
我希望它能让你羡慕地去尝试它😘。
下一篇博文将通过几张技术草图来挖掘 Raku 的历史片段(和实施考古学) 💃
我花了很多时间来收集这些死亡世界的痕迹,但相信我这是非常有趣的,所以让自己被引导吧。