2016-10-20 号更新。
General
Rakudo 和 Perl 6 的区别是什么?
Rakudo 是 Perl 6 的一个实现。目前它是完成度最好的但是过去也有其它的实现, 将来也可能会有其它实现。Perl 6 是语言的定义。很多场合
这两个名字可以宽松地使用并互相替换。
会有 Perl 6 版本 6.0.0 吗?
第一个稳定语言版本的版本称为 v6.c,而不是 6.0.0。 不同的命名方案使得不太可能发布具有精确版本 6.0.0 的语言。
您可以使用下面的代码检查您的 Rakudo 编译器是当前至少是什么版本(注意这可能不是真正的供应商二进制文件):
perl6 -e 'say q[too old] if $*PERL.version before Version.new(q[6.c])'
它首先由 Rakudo Perl 6 编译器版本的 2015.12 实现,并且可能通过使用 'use 6.c' 指令在可预见的未来支持后续版本。 下一个语言版本(无发布日期)为 v6.d.
作为一个 Perl 6 初学者我应该安装什么?
如果你是一个 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 工具。
从哪里能找到关于 Perl 6 的好文档?
最令人信赖的信息能在 perl6.org 或那儿的直接链接。
你也可以使用 Google 搜索 Freenode #perl6 IRC 频道。
http://www.perl6.org/documentation/ 和 http://doc.perl6.org/
什么是 Perl 6 spec?
"spec" 指的是 Perl 6的官方测试套件。它被称作 roast 并被托管在 github 上.
它被用来测量一个 Perl 6 的实现有多彻底。
有没有 Perl 6 的术语相关的项目?
查看 glossary
我是一个 Perl 5 程序员. Perl 5 和 Perl 6 的区别在哪儿?
在 https://docs.perl6.org/language/5to6-nutshell 下面查看 ‘5to6-nutshell’ pod 文档和相关页面。
模块
Perl 6 有 CPAN 吗? 或者 Perl 6 会使用 Perl 5 的 CPAN 吗?
Perl 6 还没有像 CPAN 那样成熟的模块仓库. 但是 modules.perl6.org 有很多已知的 Perl 6 模块, panda 能在 Rakudo 上安装这些模块.
我能在 Perl 6 中使用 Perl 5的模块吗?
使用 Inline::Perl5 能让大部分 Perl 5 模块工作, 它甚至能很好地运行 Perl 5 的 Catalyst 和 DBI。
我能在 Perl 6 中使用 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?
使用 perl6 --target=ast -e 'very-short-example()'
来抓取编译单元的抽象语法树(AST)。
语言特性
我怎么 dump Perl 6 的数据结构(就像 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。
我怎么在 Perl 6 提示符(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。 在 Perl 6 中, 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 是什么东东?
在 Perl 6 中, 类和其它类型是对象, 并且传递自身类型的类型检测。
例如如果你声明一个变量
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
派生来的, 代表着任何类型的 Perl 6 值. 主要区别是, 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++
是怎么工作的?
在 Perl 6 中, 很多基本类型是不可变的, 但是保存它们的变量不是. s///
作用于变量上, 在这个变量中放入一个新创建的字符串对象. 同样地, $i++
作用于 $i
变量上, 而不是作用在它里面的值身上.
更多详情请查看: containers 文档。
什么是数组引用和自动解引用? 我仍然需要 @ 符号吗?
在 Perl 6 中, 几乎所有的东西都是引用. 所以谈论 taking references 没有多大意义. 不像 Perl 5 那样, Perl 6 的标量变量也能直接包含数组:
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>";
Perl 6 认为 $foo
是一个散列而 <html-tag>
是一个字符串字面量的散列键。使用闭包来帮助你理解吧。
my $foo = "abc";
say "{$foo}<html-tag>";
Perl 6 有协程吗? 什么是 yield ?
Perl 6 没有 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 怎么不同, 为什么不同?
最明显的区别是, say
和 put
在输出后面添加了一个换行符, 而 print
没有.
但是还有另外一个区别: print
和 put
通过对每一个传递来的 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
优化的用于调试和向人们展示, print
和 put
更适合于产生用于其它程序的输出.
put
因此是 print
和 say
之间的一种混合; 像 print
, 它的输出适合于其它程序, 也像 say
, 它在输出的末尾添加了换行符。
token 和 rule 之间的区别是什么?
regex
, token
和 rule
这三个都引入了正则表达式, 但是语义略微有一点不同.
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 函数来告诉你这是在空上下文, 标量上下文,还是在列表上下文中调用的. Perl 6 没有与之等价的结构, 因为上下文不是向内流动的, 例如, 子例程不知道调用所在的上下文.
一个愿意是因为 Perl 6 有多重分派, 在这样一个例子中:
multi w(Int $x) { say 'Int' }
multi w(Str $x) { say 'Str' }
w(f());
没办法决定子例程 f
的调用者想要一个字符串还是想要一个整数, 因为它还不知道调用者是什么. 通常这要求解决 halting 问题, 在这个问题上, 即使写 Perl 6编译器的人也会遇到麻烦.
在 Perl 6 中达到上下文敏感的方式是返回一个知道怎样响应方法调用的对象.
例如, regex 匹配返回 Match 对象, 该对象知道怎样响应列表索引, 散列索引, 并能变成匹配的字符串.
Pointer 和 OpaquePointer 的区别是声明?
OpaquePointer
被废弃了并且已经用 Pointer
代替了。
Perl 6 实现
哪个 Perl 6 的实现是可用的?
当前开发最好的是 Rakudo(使用多个虚拟机后端)。历史上的实现还包括 Niecza (.NET) 和 Pugs (Haskell). 其它的列出在 Perl 6 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如果你想排除复数.
元问题和宣传
Perl 6 什么时间会准备好? 就是现在吗?
编程语言和它们的编译器的准备就绪不是一个二元决策. 因为它们(语言和实现)能进化, 它们平稳地发展变得更可用. 根据你对编程语言的要求, 它可能适合也可能不适合你.
请查看 功能对比矩阵 了解更详尽的实现了的功能.
请注意, Larry Wall 已经在 FOSDEM 2015 会议上宣布, 一个产品级的 Rakudo Perl 6 将会在 2015 圣诞节发布.
为什么我要学习 Perl 6? 它有什么了不起的吗?
Perl 6 统一了很多其它编程语言中不经常有的伟大想法. 虽然其中的几种语言提供了其中的某些功能, 但是没有提供全部.
不像大部分语言那样, 它提供了:
- Perl 6 提供了过程式的, 面向对象的和函数式编程方法。
- 易于使用的一致性语法, 数据结构中的符号不变性。
- 完全基于字素的 Unicode 支持, 包括附件 #29
- 足够清晰的正则表达式, 更易读, 更多功能。
- Junctions 允许多个可能性的简单检测, 例如 $a == 1|3|42(意思是 $a 等于 1 或 3 或 42)
- 相对于全局变量, 动态作用域变量提供了词法作用域备选
- 强调可组合性和本地作用域以阻止「超距作用」。例如, imports 总是本地作用域的。
- 易于理解的一致性作用域规则和闭包
- 强大的面向对象, 含有类和 roles(所有的东西都可以当做对象)。继承、子类型、代码复用。
- 内省到对象和元对象中(叠罗汉)
- 元对象协议允许元编程而不需要生成/解析代码。
- 子例程和方法签名,便于解包位置参数和命名参数。
- 根据元数,类型和可选的额外代码使用不同的签名对同一具名子例程/方法进行多重分派。
- 未知子例程/不可能的分派在编译时给出错误报告。
- 可选的渐进类型检查,无需额外的运行时成本。 还有可选类型注解。
- 基于对编译器/运行时状态的内省的高级错误报告。这意味着更有用,更精确的错误信息。
- Phasers(如 BEGIN/END) 允许代码作用域 进入/退出, 首次循环/last/next 和其它更多上下文中执行。
- 高级并发模型,用于隐式以及显式多进程处理,这超越了原始线程和锁。 Perl 6 的并发提供了一组丰富的(可组合的)工具。
- 多核计算机越来越多地被使用,由于并行性使得 Perl 6 可以使用多核,包括隐式(例如使用>>.方法)和显式 (start {code}) 。这很重要,因为摩尔定律正在结束。
- 提供结构化语言支持以实现异步执行代码的编程。
- Supplies 允许在发生某些事情时执行代码(如定时器,信号或文件系统事件)。
- react/whenever/supply 关键字允许容易地构建交互式,事件驱动的应用程序。
- 懒惰求值,如果可能的话,急切求值当需要或必要时。这意味着,例如,惰性列表,甚至无限延迟列表,如斐波纳契序列或所有素数。
- 原生数据类型用于更快的处理
- 使用 NativeCall 连接到 C/C++ 中的外部库非常简单。
- 使用 Inline::Perl5 和 Inline::Python 连接 Perl 5(CPAN)/Python 非常简单。
- 可以同时安装和加载模块的多个版本。
- 由于更简单的更新/升级策略,简化了系统管理。
- 简单的数值计算没有损失精度,因为 Rats(有理数)。
- 用于解析数据或代码的可扩展语法(Perl 6 用它解析自身)
- Perl 6 是一种非常易变的语言(定义自己的函数,运算符,traits 和数据类型,为您修改解析器)。
- 很多的数据类型选择,加上创建自己的类型的可能性。
- 具有适当边界检查的多维成型的和/或原生数组
- 在某个匹配出现时, 词法解析期间随时执行代码
- 添加自定义运算符或添加 trait 特征和编写子例程一样简单。
- 在任何运算符(系统或自定义添加的)上自动生成超运算符。
- 运行在各种后端上。目前 MoarVM 和 JVM,JavaScript在开发中,可能会有更多。
- 执行期间(JIT)热代码路径的运行时优化。
- 运行在小型(例如 Raspberry Pi)和大型多处理器硬件上。
- 基于垃圾收集:没有及时销毁,所以引用计数没有必要。使用 phasers 用以及时的动作。
- 方法可以在运行时混合到任何实例化的对象中,例如。以允许添加带外数据。
- 通过使用具有多重分派和自动生成使用信息的 MAIN 子例程,使命令行接口易于访问。
- 更少的代码行创建更紧凑的程序。名字的霍夫曼编码允许更好的可读性。
- 使用简单的迭代器接口定义的惰性列表,任何类可以通过最小化的提供单个方法来提供。
- Perl 6 的座右铭与 Perl一直保持不变:Perl是不同的。简而言之,Perl旨在"使容易的工作变得容易,使困难的工作变得可能"。和"条条大路通罗马"。现在有更多 -Ofun 添加进来。
请查看 功能比较矩阵 获取更多信息.
为什么不把它叫做除了 Perl 以外的其它东西?
很多人建议, Perl 6 跟之前的 Perl 版本的区别太大了, 我们应该考虑给它改名, 或者考虑到 Perl 6 伤害了 Perl 5, 仅仅拥有同样的名字却有更高的版本号.
Perl 6 仍然叫做 “Perl" 的主要原因是:
- Perl 6 仍然是一个 perlish 风格的语言, 和之前的版本遵守相同的底层思想(用于微型命名空间的符号, 条条大路通罗马, 吸收了很多自然语言的思想..)
- Perl 6 的代码很 perlish.
- Perl 仍然是一个强健的品牌名, 我们不想马上抛弃它
- 找到一个替代的名字很困难. 而且, “camelia” 和 “rakudo" 不是合适的编程语言名
- 即使 Perl 6 更改了它的名字, Perl 5 也不大可能增加它的版本号为 6.因为 Perl 6 已经根植于人们的头脑中了
Perl 6 对我来说足够快了吗?
那取决于你正在做什么。Perl 6 一直奉行“做对的事情然后做的更快”的哲学进行开发。对于某些东西来说它够快了, 但是需要做的更多。
Perl 6 大部分是由志愿者开发的, 但是 Perl 6 的性能在不久的将来有待提高, 因为 MoarVM 后端包含一个现代的即时(JIT)编译器。
Perl 5 程序员应该意识到 Perl 6 在面向对象方面有很多内建函数并且还有更多其它的。
简单的基准测试会误导除非你在你的 Perl 5脚本中包含了诸如 Moose, 类型检测模块等。
下面这个粗超的基准测试, 使用了所有诸如此类的一般说明, 能展示 Perl 6 在某些类似任务上能和 Perl 5的速度接近。
在你的系统上尝试下, 你可能会感到很惊讶!
# Perl 6 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/Perl 6'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 perl6
my ($prev, $current) = (1, 0);
for (0..100_000) {
($prev, $current) = ($current, $prev + $current);
}
print $current;