Wait the light to fall

Raku Features

焉知非鱼

Raku Features

(这是同一作者的法语文章 Raku en 2020 的翻译/改进版。)

感谢 Elizabeth Mattijsen 帮助校对和提供建议。

目录 #

在这两篇博文中的第一篇博文中,你会发现一个总的介绍,包括一些社区的见解和关于解释器和虚拟机(Rakudo 和 MoarVM) 的细节。

在第二部分中,你会得到一个关于 Raku 功能的长篇描述,以发现为什么 Raku 值得讨论和学习。

(下一篇博文是关于过去的实现和历史细节)

这篇博文是用爱写的 ❤️

关于 Raku #

Raku 是一种强大而成熟的编程语言,它很难实现(这将在本篇文章的第二部分介绍)。

Raku née “Perl 6” 不再是 Perl 5 的下一个版本(将是 Perl 5.36 或 Perl 7)。Raku 已经在 2019 年被重新命名,以明确这一状态。但 Raku 与 Perl 5 有很多共同之处。

最近,Raku 已经与 Perl 5 有了很大的不同,我们可以简单地用 “Perl” 来指定 Perl 5(或 Perl 7),而在任何情况下,我们更愿意用 “Raku” 来谈论 Perl 6。有一段时间,Perl 6 社区略微反对这种重命名,但整体意见发生了变化,重命名在 2018 年和 2019 年之间顺利完成(详情见本期的下一篇博文)。

你可以通过订阅《Raku 周刊》来关注 Raku 新闻。

Raku 有一个小的社区,但有一些"杀手级应用"。

  • 有一个 IDE “Comma"。

  • Cro,一个用于网络的工具箱(应用程序、服务)。

  • Sparrow,一个自动化框架,以及它的模块

  • 一个(小的)CPAN

来自 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 的知识。

一些链接 #

技术细节从这里开始,我希望你准备好了 😀

Raku 的力量 #

Raku 的功能令人印象深刻,我想从这个开始 :)

Raku 源于 Larry Wall 的语言技能,因此 Raku 受自然语言的影响很大。Raku 是多范式的(过程式、函数式、并发式编程和面向对象)。

Raku 的价值观可以在 Raku 宣言中找到 (1, 2, 3)。

像它的表亲 Perl 一样。

  • 它是一种具有上下文概念的丰富语言。
  • 它的正则表达式非常强大,Raku 甚至将 grammar 处理直接集成到语言中。
  • 它有一个 CPAN

与 Perl 5 相反。

  • 与原生代码的接口非常简单。
  • 面向对象的编程集成在了语言中(框架+对象类型)。
  • 该语言可以处理并发问题,并为其提供了一个完整的工具箱。
  • Unicode 是"默认"处理的。

对于更多的技术细节,这里有 Raku 的术语表编译器之间的功能比较(它提供了一个很好的语言概述)。在接下来的段落中,将更详细地解释 Raku 的功能。

courses.raku.orgraku.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; }
  • 带有属性 rwcopy:
sub make42($foo is rw) { $foo = 42; }
  • 多重分派:
multi sub sayit( Int $n ) { say "Number: $n"; } 
multi sub sayit( Str $s ) { say "String: $s"; }

分派是基于参数的数量、它们的类型、约束条件(我们甚至可以用函数 nextsamenextwithcallsamecallwith 来玩)。 参见"正确的窄分析"以获得最佳的候选者解析。

  • 剩余参数的吞噬和参数的扁平化(像在 Perl 5 中)。

Operators 运算符 #

  • Raku 有大量的运算符,请看完整的列表。

Periodic table of the operators

异步、并行和并发 #

一个完整的工具箱,用于处理异步、并行和并发

面向对象编程和元对象编程 #

有能力定义我们自己的操作符(见上文)和功能。元对象编程在虚拟机中被部分处理。

  • 单一或多重继承。
  • 自省。
  • 实例化后"编辑"一个类(例子)。
  • 方法的组合/封装
  • 信任关系,允许访问另一个(父)类的私有方法而不使其继承。
  • 代理 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") {}; 
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 Moar

下面是另一张架构图,取自关于 Rakudo/MoarVM 优化的演讲(2014)。

Rakudo Architecture

结论 #

这第一篇博文更多的是处理关于 Raku 的当前信息。

我希望它能让你羡慕地去尝试它😘。

下一篇博文将通过几张技术草图来挖掘 Raku 的历史片段(和实施考古学) 💃

我花了很多时间来收集这些死亡世界的痕迹,但相信我这是非常有趣的,所以让自己被引导吧。