FAQ

源文件可以在 githubraku.org上找到.

General

Rakudo 和 Raku 的区别是什么?

Rakudo 是 Raku 的一个实现。目前它是完成度最好的但是过去也有其它的实现, 将来也可能会有其它实现。Raku 是语言的定义。很多场合

这两个名字可以宽松地使用并互相替换。

会有 Raku 版本 6.0.0 吗?

第一个稳定语言版本的版本称为 v6.c,而不是 6.0.0。 不同的命名方案使得不太可能发布具有精确版本 6.0.0 的语言。

您可以使用下面的代码检查您的 Rakudo 编译器是当前至少是什么版本(注意这可能不是真正的供应商二进制文件):

raku -e 'say q[too old] if $*PERL.version before Version.new(q[6.c])'

它首先由 Rakudo Raku 编译器版本的 2015.12 实现,并且可能通过使用 ‘use 6.c’ 指令在可预见的未来支持后续版本。 下一个语言版本(无发布日期)为 v6.d.

作为一个 Raku 初学者我应该安装什么?

如果你是一个 Linux 或 Mac 用户, 你可能需要下载 Rakudo Star 并通过编译 MoarVM 版本安装(一个简单的处理)

如果你是一个 Windows 32 或 64 位用户, 那么 Rakudo Star 二进制版本在 rakudo 网站也能获得。你需要 Windows Git 来使用 panda。

Linux 和 Mac 二进制版本稍后也可能从供应商和第三方那儿获取到。尽管供应商版本可能过时了。

或者有一个官方的 rakudo star Docker 镜像, 地址为 https://hub.docker.com/_/rakudo-star/

作为一个中高级用户我想跟进 Rakudo 开发

安装类似于 Perl 5 的 perlbrew – rakudobrew , 同等的 Python 还有 Ruby 工具。

从哪里能找到关于 Raku 的好文档?

最令人信赖的信息能在 raku.org 或那儿的直接链接。

你也可以使用 Google 搜索 Freenode #raku IRC 频道。

http://www.raku.org/documentation/http://doc.raku.org/

什么是 Raku spec?

“spec” 指的是 Raku的官方测试套件。它被称作 roast 并被托管在 github 上.

它被用来测量一个 Raku 的实现有多彻底。

有没有 Raku 的术语相关的项目?

查看 glossary

我是一个 Perl 5 程序员. Perl 5 和 Raku 的区别在哪儿?

https://docs.raku.org/language/5to6-nutshell 下面查看 ‘5to6-nutshell’ pod 文档和相关页面。

模块

Raku 有 CPAN 吗? 或者 Raku 会使用 Perl 5 的 CPAN 吗?

Raku 还没有像 CPAN 那样成熟的模块仓库. 但是 modules.raku.org 有很多已知的 Raku 模块, panda 能在 Rakudo 上安装这些模块.

我能在 Raku 中使用 Perl 5的模块吗?

使用 Inline::Perl5 能让大部分 Perl 5 模块工作, 它甚至能很好地运行 Perl 5 的 Catalyst 和 DBI。

我能在 Raku 中使用 C 和 C++ 吗?

Nativecall 让这个特别容易。

Nativecall 找不到 libfoo.so 并且我只有 libfoo.so.1.2!

这在 Debian 那样的系统中很常见。 你需要安装 “libfoo-dev” 来为丢失的文件设置符号链接。

所有的传统 Unix 库函数去哪儿了?

使用 Nativecall 访问它们很容易。 POSIX 模块也可以。

Rakudo 有核心标准库吗?

Rakudo 是一个包含最小电量的编译器发布(Test 和 Nativecall等等),像 linux 内核一样。

Rakudo Star 是一个带有一些有用模块的 rakudo, 并且更多的模块可以从生态系统里安装。

有像 B::Deparse 那样的东西吗?/我怎么抓住 AST?

使用 raku --target=ast -e 'very-short-example()' 来抓取编译单元的抽象语法树(AST)。

语言特性

我怎么 dump Raku 的数据结构(就像 Perl 5 的 Data::Dumper 和类似的)?

examples:


my $foo="bar"
dd $foo        # Str $foo = "bar"
say :$foo.perl # :foo("bar")
say :$foo.gist # foo => bar

生态系统中还有模块来做这个事情, 例如 Data::Dump 使用颜色来 Dump。

我怎么在 Raku 提示符(REPL)中找到历史命令行?

从生态系统中安装 Linenoise.

作为一种选择, 在 UNIX 那样的系统中可以安装 rlwrap。这在类 Debian 系统中可以通过apt-get install rlwrap 安装。

为什么 Rakudo 编译器有时候报错更友好?

如果在输出中出现 SORRY! , 则错误是编译时错误, 否则是运行时错误。

Examples:


say 1/0     # Attempt to divide 1 by zero using div

sub foo ( Int $a, Int $b ) {...}
foo(1)      # ===SORRY!=== Error while compiling ...

什么是 (Any)?

Any 是一个用于新类的默认超类(superclass)的顶层类。 它经常在这样的上下文出现:变量被定义但没有被赋值, 这里它类似于其它语言中的 undef 或 null 值。

examples:

my $foo;
say $foo;       # (Any) 注意圆括号表明的类型对象
say $foo.^name  # Any

(Any) 不应该被用于检查 definedness。 在 Raku 中, definedness 可能是一个对象的属性。 通常实例是被定义的, 而类型对象是未定义的。

say 1.defined       # True
say (Any).defined   # False

so 是什么?

so 是一个松散优先级的操作符, 它强制上下文为 Bool.

so 拥有和 ? 前缀操作符同样的语义, 就像 and&& 的低优先级版本一样.

用法示例:

say so 1|2 == 2;    # Bool::True

在这个例子中, 比较的结果(结果是 Junction)在打印之前被转换为 Bool 值了.

签名中的那些 :D 和 :U 是什么东东?

在 Raku 中, 类和其它类型是对象, 并且传递自身类型的类型检测。 例如如果你声明一个变量

my Int $x = 42;

那么, 你不仅可以给它赋值整数(即, Int 类的实例), 还能给它赋值 Int 类型对象自身:

$x = Int

如果你想排除类型对象, 你可以追加一个 :D 类型微笑符, 它代表"定义”(definite):

my Int:D $x = 42;
$x = Int;  # dies with:
           # Type check failed in assignment to $x;
           # expected Int:D but got Int

同样地, :U 约束为未定义的值, 即类型对象。 要显式地允许类型对象或实例, 你可以使用 :_

签名中的 –> 是什么东东?

--> 是一个返回值约束, 要么是类型要么是有定义的值。

类型约束的例子:

sub divide-to-int( Int $a, Int $b --> Int ) {
        return ($a / $b).narrow;
}

divide-to-int(3, 2)
# Type check failed for return value; expected Int but got Rat

有明确返回值的例子:

sub discard-random-number( --> 42 ) { rand }
say discard-random-number
# 42

在这种情况下,最终值被抛弃,因为已经指定了返回值。

Any 和 Mu 的区别是什么?

Mu 是所派生出的所有其它类型的基类型. Any 是从 Mu派生来的, 代表着任何类型的 Raku 值. 主要区别是, Any 不包含 Junction.

子例程参数的默认类型是 Any, 以至于当你声明 sub foo ($a) 时, 你真正表达的是 sub foo (Any $a) . 类似地, 类的声明被假定继承自 Any, 除非使用了像 is Mu 这样的 trait 特征.

怎么从 Junction 中提取值?

如果你想从 Junction 中提取值(特征态), 那你可能正误入歧途. 应该使用 Set 代替

Junctions 作为匹配器, 而不是使用它们做代数.

如果你还是想那样做, 你可以滥用自动线程(autothreading):

sub eigenstates(Mu $j) {
    my @states;
    -> Any $s { @states.push: $s }.($j);
    @states;
}

say eigenstates(1|2|3).join(', ');
# prints 1, 2, 3 or a permutation thereof

如果 Str 是不可变的, 那么 s/// 是怎么工作的? 如果 Int 是不可变的, $i++ 是怎么工作的?

在 Raku 中, 很多基本类型是不可变的, 但是保存它们的变量不是. s/// 作用于变量上, 在这个变量中放入一个新创建的字符串对象. 同样地, $i++ 作用于 $i 变量上, 而不是作用在它里面的值身上.

更多详情请查看: containers 文档。

什么是数组引用和自动解引用? 我仍然需要 @ 符号吗?

在 Raku 中, 几乎所有的东西都是引用. 所以谈论 taking references 没有多大意义. 不像 Perl 5 那样, Raku 的标量变量也能直接包含数组:

my @a = 1, 2, 3;
say @a;                 # "1 2 3\n"
say @a.WHAT;            # (Array)

my $scalar = @a;
say $scalar;            # "1 2 3\n"
say $scalar.WHAT;       # (Array)

最大的区别是, 标量中的数组在列表上下文中是一个值, 然而数组会被愉快地迭代:

my @a = 1, 2, 3;
my $s = @a;

for @a { ... }          # loop body executed 3 times
for $s { ... }          # loop body executed only once

my @flat = flat @a, @a;
say @flat.elems;        # 6

my @nested = flat $s, $s;
say @nested.elems;      # 2

你可以使用 @( ... ) 或通过在表达式身上调用 .list 方法来强制展平, 使用 $( ... ) 或通过在表达式身上调用 .item 方法强制为 item 上下文(不展平).

为什么还要符号? 你不能没有它们吗?

有几个原因:

  • 它们使插值变量到字符串中变得更容易
  • 它们为不同的变量和 twigils 组成了微型命名空间, 因此避免了名字冲突
  • 它们允许简单的 单数/复数 区别
  • 它们像使用强制性名词标记的自然语言一样工作,所以我们的大脑为处理它而生
  • 它们不是强制性的,因为你可以声明无符号名字(如果你不介意含糊不清)

“类型 Str 不支持关联索引”

你可能会把字符串插值和 HTML 搞混。

my $foo = "abc";
say "$foo<html-tag>";

Raku 认为 $foo 是一个散列而 <html-tag> 是一个字符串字面量的散列键。使用闭包来帮助你理解吧。

my $foo = "abc";
say "{$foo}<html-tag>";

Raku 有协程吗? 什么是 yield ?

Raku 没有 Python 那样的 yield 语句, 但是它通过惰性列表却能提供类似的功能. 有两种很潮的方式来写出能返回惰性列表的例程:

# first method, gather/take
my @values := gather while have_data() {
    # do some computations
    take some_data();
    # do more computations
}

# second method, use .map or similar method
# on a lazy list
my @squares := (1..*).map(-> $x { $x * $x });
# or
my @squares = (1..*).map(-> \x { x² });

为什么我需要反斜线(unspace)在多行上分割方法调用?

(请在这儿添加答案)

为什么我不能从 new 方法初始化私有属性, 我怎么修复它?

这样的代码:


class A {
    has $!x;
    method show-x {
        say $!x;
    }
}
A.new(x => 5).show-x;

不会打印出 5. Private 属性是私有的, 这意味着私有属性在外面是不可见的. 如果默认的构造器能够初始化私有属性, 那么这些私有属性就会泄露到公共 API 中.

如果你仍旧想让它工作, 你可以添加一个 submethod BUILD 来初始化它们:

class B {
    has $!x;
    submethod BUILD(:$!x) { }
    method show-x {
        say $!x;
    }
}
A.new(x => 5).show-x;

BUILD 由默认的构造器使用用户传递给构造器的所有具名参数调用(间接地, 更多细节查看Object Construction)。 :$!x 是名为 x 的具名参数, 当使用名为 x 的具名参数来调用时, 它的值被绑定到属性 $!x 上.

但不要这样做。如果名字是 public 的,使用 $.x 以那样的方式声明没有什么不好,因为默认情况下外部视图是只读的(readonly),你仍然可以使用 $!x 从内部访问它。

say, put 和 print 怎么不同, 为什么不同?

最明显的区别是, sayput 在输出后面添加了一个换行符, 而 print 没有.

但是还有另外一个区别: printput 通过对每一个传递来的 item 调用 Str 方法来把它的参数转换为字符串, 相反, say 使用 gist 方法. 前者是为计算机设计的, 后者是为人类.

或者它俩被解析的方式不同, $obj.Str 给出一个字符串表示, $obj.gist 是对象的一个简短总结, 适合编程人员的快速识别, $obj.perl 打印一个 Perlish 的表示.

例如, 类型对象, 也是熟知的 “未定义值”, 字符串化为一个空的字符串和警告, 而 gist 方法返回由一对圆括号包裹的类型的名字.(用于表明除了类型之外什么也没有).

my Date $x;     # $x now contains the Date type object
print $x;       # empty string plus warning
say $x;         # (Date)\n

所以, say 优化的用于调试和向人们展示, printput 更适合于产生用于其它程序的输出.

put 因此是 printsay 之间的一种混合; 像 print, 它的输出适合于其它程序, 也像 say, 它在输出的末尾添加了换行符。

token 和 rule 之间的区别是什么?

regex , tokenrule 这三个都引入了正则表达式, 但是语义略微有一点不同.

token 隐含了 :ratchet:r 修饰符, 这防止了 rule 的回溯.

rule 隐含了 :ratchet:sigspace (缩写为 :s)修饰符, 这意味着规则(rule)不会回溯, 并且它把 regex 的文本中的空白当作 <.ws> 调用(例如匹配空白, 除了在两个单词字符之间之外, 它是可选的). regex 开头的空白和备选分支中每个分支开头的空白会被忽略.

regex 声明一个简单的正则表达式,没有任何隐含的修饰符。

die 和 fail 之间的区别是什么?

die 抛出一个异常.

fail 返回一个 Failure 对象。 (如果调用者已经声明了 use fatal; 在调用作用域中, fail 会抛出一个异常而不返回)

Failure 是一个 “未知的” 或 “懒惰的” 异常.它是一个含有异常的对象, 当这个 Failure 被用作普通的对象或者在 sink 上下文中忽略它时, 则会抛出一个异常.

Failure 从 defined 检查中返回 False, 并且你可以使用 exception 方法提取出异常.

为什么 wantarray 或 want 不见了? 我能在不同的上下文中返回不同的东西吗?

Perl 拥有 wantarray 函数来告诉你这是在空上下文, 标量上下文,还是在列表上下文中调用的. Raku 没有与之等价的结构, 因为上下文不是向内流动的, 例如, 子例程不知道调用所在的上下文.

一个愿意是因为 Raku 有多重分派, 在这样一个例子中:

multi w(Int $x) { say 'Int' }
multi w(Str $x) { say 'Str' }
w(f());

没办法决定子例程 f 的调用者想要一个字符串还是想要一个整数, 因为它还不知道调用者是什么. 通常这要求解决 halting 问题, 在这个问题上, 即使写 Raku编译器的人也会遇到麻烦.

在 Raku 中达到上下文敏感的方式是返回一个知道怎样响应方法调用的对象.

例如, regex 匹配返回 Match 对象, 该对象知道怎样响应列表索引, 散列索引, 并能变成匹配的字符串.

Pointer 和 OpaquePointer 的区别是声明?

OpaquePointer 被废弃了并且已经用 Pointer 代替了。

Raku 实现

哪个 Raku 的实现是可用的?

当前开发最好的是 Rakudo(使用多个虚拟机后端)。历史上的实现还包括 Niecza (.NET) 和 Pugs (Haskell). 其它的列出在 Raku Compilers 下面。

Rakudo 是用什么语言写的?

NQP 是(1)NQP 代码,(2)底层虚拟机使用的任何语言,(3)一些第三方 C 和 Java 库,以及(4)早期运行构建过程创建的一些引导文件的混合 。

为什么我不能把所有的数值都赋值给 Num 类型的变量?

my Num $x = 42;
# dies with
# Type check failed in assignment to '$x'; expected 'Num' but got 'Int'

Num 是浮点类型, 与 integers 不兼容. 如果你想要一个允许任何数字值的类型约束, 使用 Numeric (它也允许复数), 或 Real如果你想排除复数.

元问题和宣传

Raku 什么时间会准备好? 就是现在吗?

编程语言和它们的编译器的准备就绪不是一个二元决策. 因为它们(语言和实现)能进化, 它们平稳地发展变得更可用. 根据你对编程语言的要求, 它可能适合也可能不适合你.

请查看 功能对比矩阵 了解更详尽的实现了的功能.

请注意, Larry Wall 已经在 FOSDEM 2015 会议上宣布, 一个产品级的 Rakudo Raku 将会在 2015 圣诞节发布.

为什么我要学习 Raku? 它有什么了不起的吗?

Raku 统一了很多其它编程语言中不经常有的伟大想法. 虽然其中的几种语言提供了其中的某些功能, 但是没有提供全部.

不像大部分语言那样, 它提供了:

  • Raku 提供了过程式的, 面向对象的和函数式编程方法。
  • 易于使用的一致性语法, 数据结构中的符号不变性。
  • 完全基于字素的 Unicode 支持, 包括附件 #29
  • 足够清晰的正则表达式, 更易读, 更多功能。
  • Junctions 允许多个可能性的简单检测, 例如 $a == 1|3|42(意思是 $a 等于 1 或 3 或 42)
  • 相对于全局变量, 动态作用域变量提供了词法作用域备选
  • 强调可组合性和本地作用域以阻止「超距作用」。例如, imports 总是本地作用域的。
  • 易于理解的一致性作用域规则和闭包
  • 强大的面向对象, 含有类和 roles(所有的东西都可以当做对象)。继承、子类型、代码复用。
  • 内省到对象和元对象中(叠罗汉)
  • 元对象协议允许元编程而不需要生成/解析代码。
  • 子例程和方法签名,便于解包位置参数和命名参数。
  • 根据元数,类型和可选的额外代码使用不同的签名对同一具名子例程/方法进行多重分派。
  • 未知子例程/不可能的分派在编译时给出错误报告。
  • 可选的渐进类型检查,无需额外的运行时成本。 还有可选类型注解。
  • 基于对编译器/运行时状态的内省的高级错误报告。这意味着更有用,更精确的错误信息。
  • Phasers(如 BEGIN/END) 允许代码作用域 进入/退出, 首次循环/last/next 和其它更多上下文中执行。
  • 高级并发模型,用于隐式以及显式多进程处理,这超越了原始线程和锁。 Raku 的并发提供了一组丰富的(可组合的)工具。
  • 多核计算机越来越多地被使用,由于并行性使得 Raku 可以使用多核,包括隐式(例如使用».方法)和显式 (start {code}) 。这很重要,因为摩尔定律正在结束。
  • 提供结构化语言支持以实现异步执行代码的编程。
  • Supplies 允许在发生某些事情时执行代码(如定时器,信号或文件系统事件)。
  • react/whenever/supply 关键字允许容易地构建交互式,事件驱动的应用程序。
  • 懒惰求值,如果可能的话,急切求值当需要或必要时。这意味着,例如,惰性列表,甚至无限延迟列表,如斐波纳契序列或所有素数。
  • 原生数据类型用于更快的处理
  • 使用 NativeCall 连接到 C/C++ 中的外部库非常简单。
  • 使用 Inline::Perl5 和 Inline::Python 连接 Perl 5(CPAN)/Python 非常简单。
  • 可以同时安装和加载模块的多个版本。
  • 由于更简单的更新/升级策略,简化了系统管理。
  • 简单的数值计算没有损失精度,因为 Rats(有理数)。
  • 用于解析数据或代码的可扩展语法(Raku 用它解析自身)
  • Raku 是一种非常易变的语言(定义自己的函数,运算符,traits 和数据类型,为您修改解析器)。
  • 很多的数据类型选择,加上创建自己的类型的可能性。
  • 具有适当边界检查的多维成型的和/或原生数组
  • 在某个匹配出现时, 词法解析期间随时执行代码
  • 添加自定义运算符或添加 trait 特征和编写子例程一样简单。
  • 在任何运算符(系统或自定义添加的)上自动生成超运算符。
  • 运行在各种后端上。目前 MoarVM 和 JVM,JavaScript在开发中,可能会有更多。
  • 执行期间(JIT)热代码路径的运行时优化。
  • 运行在小型(例如 Raspberry Pi)和大型多处理器硬件上。
  • 基于垃圾收集:没有及时销毁,所以引用计数没有必要。使用 phasers 用以及时的动作。
  • 方法可以在运行时混合到任何实例化的对象中,例如。以允许添加带外数据。
  • 通过使用具有多重分派和自动生成使用信息的 MAIN 子例程,使命令行接口易于访问。
  • 更少的代码行创建更紧凑的程序。名字的霍夫曼编码允许更好的可读性。
  • 使用简单的迭代器接口定义的惰性列表,任何类可以通过最小化的提供单个方法来提供。
  • Perl 6 的座右铭与 Perl一直保持不变:Perl是不同的。简而言之,Perl旨在"使容易的工作变得容易,使困难的工作变得可能”。和"条条大路通罗马”。现在有更多 -Ofun 添加进来。

请查看 功能比较矩阵 获取更多信息.

为什么不把它叫做除了 Perl 以外的其它东西?

很多人建议, Raku 跟之前的 Perl 版本的区别太大了, 我们应该考虑给它改名, 或者考虑到 Raku 伤害了 Perl 5, 仅仅拥有同样的名字却有更高的版本号.

Raku 仍然叫做 “Perl” 的主要原因是:

  • Raku 仍然是一个 perlish 风格的语言, 和之前的版本遵守相同的底层思想(用于微型命名空间的符号, 条条大路通罗马, 吸收了很多自然语言的思想..)
  • Raku 的代码很 perlish.
  • Perl 仍然是一个强健的品牌名, 我们不想马上抛弃它
  • 找到一个替代的名字很困难. 而且, “camelia” 和 “rakudo” 不是合适的编程语言名
  • 即使 Raku 更改了它的名字, Perl 5 也不大可能增加它的版本号为 6.因为 Raku 已经根植于人们的头脑中了

Raku 对我来说足够快了吗?

那取决于你正在做什么。Raku 一直奉行“做对的事情然后做的更快”的哲学进行开发。对于某些东西来说它够快了, 但是需要做的更多。 Raku 大部分是由志愿者开发的, 但是 Raku 的性能在不久的将来有待提高, 因为 MoarVM 后端包含一个现代的即时(JIT)编译器。 Perl 5 程序员应该意识到 Raku 在面向对象方面有很多内建函数并且还有更多其它的。 简单的基准测试会误导除非你在你的 Perl 5脚本中包含了诸如 Moose, 类型检测模块等。

下面这个粗超的基准测试, 使用了所有诸如此类的一般说明, 能展示 Raku 在某些类似任务上能和 Perl 5的速度接近。 在你的系统上尝试下, 你可能会感到很惊讶!

# Raku version
use v6;

class Foo { has $.i is rw };

for (1..1_000_000) -> $i {
    my $obj = Foo.new;
    $obj.i = $i;
}

# Perl 5 version
package Foo;
use Moose;

has i => (is => 'rw');

__PACKAGE__->meta->make_immutable;

for my $i (1..1_000_000) {
    my $obj = Foo->new;
    $obj->i($i);
}

1;

# Another Perl 5 version that offers bare-bones set of features
# compared to Moose/Raku's version but those are not needed in this
# specific, simple program anyway.
package Foo;
use Mojo::Base -base;

has 'i';

for my $i (1..1_000_000) {
    my $obj = Foo->new;
    $obj->i($i);
}

1;

# A perl program which works under both perl5 (with perl -Mbigint)
# and raku

my ($prev, $current) = (1, 0);

for (0..100_000) {
    ($prev, $current) = ($current, $prev + $current);
}
print $current;