拉里·沃尔:启示录二
编者按:这篇启示录已过时,但出于历史原因仍在此处保留。请参阅摘要02以获取最新信息。
Perl 6 启示录 |
“启示录”系列的其余部分可在拉里·沃尔的作者页面上找到。 |
目录
拉里·沃尔将在今年的开源大会上发表他的年度娱乐性演讲,讨论Perl 5和Perl 6的现状。不要错过这个难得的机会,听听Perl、补丁和运行创造者的见解。 |
这是启示录2,旨在与《骆驼书》第二章一起阅读。基本假设是,如果第二章讨论了我在这里没有讨论的内容,那么它不会在Perl 6中改变。(当然,这可能是疏忽。有人可能会说,那些疏忽的人有一种疏忽的才能。)
在进一步讨论之前,我想感谢所有RFC过程的受害者,哦,参与者。(我请求特别宽恕那些我无法很好地进入大脑以吸收他们想法的人)。我还要特别感谢达米安·康威,他将在其中识别出许多他的系统性想法,包括一些由于我的干预而没有得到改善的想法。
以下是涵盖的RFC
RFC PSA Title
--- --- -----
Textual
005 cdr Multiline Comments for Perl
102 dcr Inline Comments for Perl
Types
161 adb Everything in Perl Becomes an Object
038 bdb Standardise Handling of Abnormal Numbers Like Infinities and NaNs
043 bcb Integrate BigInts (and BigRats) Support Tightly With the Basic Scalars
192 ddr Undef Values ne Value
212 rrb Make Length(@array) Work
218 bcc C<my Dog $spot> Is Just an Assertion
Variables
071 aaa Legacy Perl $pkg'var Should Die
009 bfr Highlander Variable Types
133 bcr Alternate Syntax for Variable Names
134 bcc Alternative Array and Hash Slicing
196 bcb More Direct Syntax for Hashes
201 bcr Hash Slicing
Strings
105 aaa Remove "In string @ must be \@" Fatal Error
111 aaa Here Docs Terminators (Was Whitespace and Here Docs)
162 abb Heredoc Contents
139 cfr Allow Calling Any Function With a Syntax Like s///
222 abb Interpolation of Object Method Calls
226 acr Selective Interpolation in Single Quotish Context
237 adc Hashes Should Interpolate in Double-Quoted Strings
251 acr Interpolation of Class Method Calls
252 abb Interpolation of Subroutines
327 dbr C<\v> for Vertical Tab
328 bcr Single Quotes Don't Interpolate \' and \\
Files
034 aaa Angle Brackets Should Not Be Used for File Globbing
051 ccr Angle Brackets Should Accept Filenames and Lists
Lists
175 rrb Add C<list> Keyword to Force List Context (like C<scalar>)
Retracted
010 rr Filehandles Should Use C<*> as a Type Prefix If Typeglobs Are Eliminated
103 rr Fix C<$pkg::$var> Precedence Issues With Parsing of C<::>
109 rr Less Line Noise - Let's Get Rid of @%
245 rr Add New C<empty> Keyword to DWIM for Clearing Values
263 rr Add Null() Keyword and Fundamental Data Type
原子
Perl 6 程序在概念上是使用 Unicode 编写的,并且在默认情况下假设 Unicode 语义,即使它们在后台处理其他字符集。请注意,当我们说 Perl 是用 Unicode 编写的,我们指的是一个抽象字符集,而不是任何特定的编码。(在西方,典型的程序可能使用 UTF-8 编写,而在东方则可能使用某些 16 位字符集。)
分子
RFC 005:Perl 的多行注释
我承认在这个问题上存在偏见——我在年少时受到了 Ada 设计理由的不当影响,我认为这很好地说明了为什么应该将多行注释从语言中排除。
但即使我没有盲目地有偏见,我也怀疑我会看看这个事物的心理学,并注意到在许多情况下,即使在有多个行注释的语言中,人们仍然倾向于像这样使用它们
/*
* Natter, natter, natter.
* Gromish, gromish, gromish.
*/
对此的反对意见当然是,人们在 C 中并不总是这样做,那么为什么他们必须在 Perl 中这样做呢?如果 Perl 中没有其他方式来使用多行注释,他们就会有更强的理由。但已经存在另一种方式,尽管这种做法被这份 RFC 作为“权宜之计”所拒绝。
但在我看来,与其添加另一种类型的注释或试图使某些看起来像代码的东西表现得像注释,不如简单地修复 POD 中存在的问题,使其用于注释不再被视为权宜之计。POD 的实际设计可以推迟到第 26 个末日,但我们可以推测,在这一点上,POD 和 Perl 之间切换的规则对于注释的使用来说是不理想的。如果是这样,那么在 Perl 6 中我们可能会有一条这样的规则:如果一个 =begin MUMBLE
从 Perl 切换到 POD 模式,那么相应的 =end MUMBLE
应该切换回(没有 =cut
指令)。
请注意,我们还没有定义我们的 MUMBLE
,但它们可以被设置起来,让我们的程序能够以我们想要的方式访问所需的数据。例如,这种类型的注释很可能与某种文献(至少,是半文献)编程框架结合在一起。
RFC 102:Perl的内联注释
我从未特别喜欢内联注释——正如通常所实践的,它们往往会使代码变得混乱,而不是使其清晰。话虽如此,“如果预先声明,一切都是公平的。”因此,不应该有任何东西阻止某人编写一个处理它们的词法分析器正则表达式,只要我们使词法分析器足够可变。我们将这样做。(实际上,字符序列“/*
”不太可能出现在标准 Perl 6 中。这可能意味着它在非标准 Perl 6 中很可能出现。:-)
声明非标准注释的祈使语句还允许人们使用 /* */
作为多行注释,如果他们喜欢。(但我仍然认为最好使用 POD 指令,以便使文本对程序可访问。)
内置数据类型
这里的基本变化是,Perl 6 不仅支持标量、数组和哈希,而且还支持不透明对象作为第四种基本数据类型。(你可以将它们视为做得正确的伪哈希。)虽然类可以以任何方式访问其对象属性,但所有外部对不透明对象的访问都通过方法进行,即使是属性也是如此。(这保证了属性继承正确工作。)
尽管Perl 6仍然默认使用无类型标量,但如果你提供更多的类型信息,Perl将能够为你提供更出色的性能和安全。基本假设是,同构数据结构将位于数组和哈希中,因此你可以声明数组和哈希中存储的标量的类型。异构结构仍然可以放入无类型数组和哈希中,但一般来说,Perl 6将鼓励你使用类来处理此类数据,就像C语言鼓励你使用结构体而不是数组来处理此类数据。
在我们详细讨论之前,我们将提到一个概念——“属性”。(在Perl 5中,我们称这些为“属性”,但如今我们保留该术语用于实际的物体属性,因此我们将这些称为“属性”)。变量和值可以与额外的数据关联,这些数据与变量或值的常规类型学无关。现在,只需将属性视为向不支持它们的类添加临时属性的一种方式。你也可以将其视为一种在单个对象粒度上的类继承形式,而无需声明一个全新的类。
RFC 161:Perl中的所有内容都成为对象。
这是一个本质上相当简短的哲学RFC,细节较少。尽管如此,我同意所有Perl对象都应像对象一样行动的前提。如果你选择不将它们视为对象,Perl也会尝试这样做。(例如,即使属性本身不在哈希中存储,你仍然可以使用哈希索引和切片语法来调用属性访问器。)仅仅因为Perl 6在内部更面向对象,并不意味着你会在不想的时候被迫以面向对象的方式思考。(总的来说,在Perl 6中,某些地方可能比Perl 5更需要面向对象的思考。例如,文件句柄在Perl 6中更面向对象,而曾经神奇地与当前选择的输出句柄关联的特殊变量现在通过关联特定的文件句柄来更好地指定。)
RFC 038:标准化处理异常数字(如无穷大和NaN)
这可能会在某些位置减慢数值处理速度。也许可以在需要时将其关闭。我们需要小心,不要发明出比IEEE浮点运算更慢的运行机制。我们还应该努力避免定义一个会使数值类型到Java或C#类型的转换变得有问题的类型系统。
话虽如此,标准语义是一个好事情,应该是默认行为。
RFC 043:紧密集成BigInts(和BigRats)与基本标量
此RFC建议使用pragma启用该功能,但我认为它可能应该与运行时类型系统相关联,这意味着它更多地由数据如何创建驱动,而不是数据存储或处理的位置。我不认为我们可以将其作为pragma,除非可能影响后续词法作用域中实际声明中的“int”和“num”的含义。
use bigint;
my int $i;
可能真的意味着
my bigint $i;
或者可能只是
my int $i is bigint;
因为表示规范可能只是被认为是“细枝末节”。但关于词法作用域变量属性指定它们所包含对象的本质的整个主题有些问题。变量是一种小型接口,可以说是程序与相关对象之间的契约。仅影响程序如何看待对象的属性不是问题——当你声明一个变量为常量时,你承诺不会通过该变量修改对象,而不是说关于对象内在真实的事情。(当然,并非所有对象都是内在常量的。)
其他属性声明可能需要在构造函数的调用中发表意见,以确保变量的对象视图与对象本身的本质之间的一致性。在最坏的情况下,我们可以在运行时尝试强制一致性,但这可能会很慢。如果将每个Dog
对象赋值给Mammal
变量时都必须检查Dog
是否是Mammal
,那么赋值就会变成狗。
因此,当我们定义变量声明和构造函数之间的关系时,我们必须重新审视这个问题。无论如何,如果我们不使Perl的数值类型自动提升到大的表示形式,我们至少应该使您在需要这样做时能够轻松地指定。
RFC 192:Undef值ne值
我拒绝了这一点,因为我认为未定义的东西应该被视为未定义。我认为标准语义对于捕获许多类型的错误是有用的。
话虽如此,但希望修改特定范围内的标准运算符将很容易,所以我认为我们的思维方式并不是唯一的思维方式,我认为。
RFC 212:使length(@array)
工作
这里有一个奇怪的地方,一个作者撤回的RFC,但我或多或少地接受了。我认为length(@array)
应该与@array.length()
等效,因此如果有一个可用的length
方法,应该调用它。
问题是是否应该在字符串或数组中存在一个length
方法。现在它对数组的合理性几乎比对字符串的合理性要大,因为当你谈论字符串的长度时,你需要知道你是在谈论字节长度还是字符长度。因此,我们可能将传统的长度函数分成两个,在这种情况下,我们可能最终得到
$foo.chars
$foo.bytes
@foo.elems
或者类似的。无论我们选择什么方法名,通过区分它们来提供上下文会更强大。例如,可以设想调用@foo.bytes
来返回所有字符串的字节长度。如果我们重载方法名,那就不会奏效。
甚至chars($foo)
可能也不够精确,因为,根据你如何处理Unicode,你可能想知道字符串的实际字符长度,不计入不占用额外空间的组合字符。但这将是另一个话题。
RF C 218:my Dog $spot
只是一个断言
我预计这种形式的声明
my Dog $spot;
仅仅是一个断言,即您将不会使用$spot
与其作为Dog
不一致。但是,“断言”一词的意思与这个RFC不同。这个断言可能或可能不在每次将$spot
赋值时进行测试,这取决于实用主义环境。这个简单的声明不调用构造函数;然而,可能存在其他形式的声明会这样做。这可能是必要的,以便变量和对象可以互相传递属性,并在一般情况下确保它们彼此一致。例如,您可能声明一个具有多维形状的数组,而这个形状属性需要对构造函数可见,如果我们不想重复指定它。
另一方面,我们可能能够足够多地重载赋值以实现相同的目标,所以我在这个问题上推迟了判断。我决定的是,上述没有参数的简单声明不会调用构造函数,而只是告诉编译器一些信息。
关于类型的其他决策
内置对象类型将全部为大写:INTEGER
、NUMBER
、STRING
、REF
、SCALAR
、ARRAY
、HASH
、REGEX
和 CODE
。与其中至少一些相对应,还会有小写内置类型,如 int
、num
、str
和 ref
。使用小写类型名意味着你不想在值上进行任何复杂的面向对象操作,或者存储任何运行时属性,因此 Perl 可以自由地将它们紧凑地存储。(作为一个极限情况,bit
类型的对象可以存储在一个位中。)这种区别大致对应于其他计算机语言的装箱/解装箱区别,但 Perl 6 很可能尽可能地尝试消除这种区别。因此,例如,一个 int
仍然可以在字符串上下文中使用,Perl 会为你转换它,但不会缓存它,所以下次你再次将其用作字符串时,它将需要再次转换。
数组或哈希的声明类型指定了每个元素的类型,而不是数组或哈希的整体类型。这一点可以通过以下观点得到证实:数组或哈希实际上只是某种奇怪类型的函数,它(典型情况下)以索引作为参数,并返回特定类型的值。如果你想将类型与数组或哈希整体关联起来,这涉及到设置一个 tie
属性。如果你发现自己希望在不同元素上声明不同的类型,那么这可能意味着你应该使用一个类来处理整个异构类型,或者至少声明数组或哈希的类型,这将作为所有包含对象的基类。
当然,无类型的数组和哈希将像现在一样被接受。但是,当强制语言在运行时延迟所有类型检查和方法查找时,语言的速度只能达到一定程度。
意图是在有用的地方使用类型信息,而在没有用的地方则不需要它。除了性能和安全外,类型信息还有另一个有用的地方是在编写其他语言的接口。假设 Perl 6 将提供足够的可选类型声明语法,在大多数情况下将不再需要编写 XS 风格的粘合剂。
变量
RFC 0 71:遗留 Perl $pkg'var 应该死亡
我同意。我在这里过分受到 Ada 语法的启发,这是一个错误。尽管我们在 Perl 6 中添加了一个类似于 Ada 的属性特性的功能特性,但我们不会重蹈覆辙,重新引入一个让高亮编辑器头疼的语法。我们将尝试犯不同的错误。
RFC 009:高lander 变量类型
我基本上同意这个 RFC 尝试解决的问题,但我不同意提出的解决方案。基本问题是,虽然 $foo[$bar]
与 @foo
而不是 $foo
的惯用关联在 Perl 4 中工作得很好,当我们为 Perl 5 添加递归数据结构时,它在记法上开始起阻碍作用,所以那个最初的奇怪字符试图做太多的事情,既要引入引用的“根”,也要应用到最后索引的上下文。这导致了看起来古怪的结构,如
$foo->[1][2][3]
这个 RFC 提议通过在名称级别统一标量变量、数组和哈希来解决这个困境。但我认为人们喜欢将 $foo
、@foo
和 %foo
视为不同的变量,所以我不想打破这一点。此外,RFC 没有统一 &foo
,而引用函数以及更普通的数据结构是完全可以的。
因此,而不是统一名称,我相信我们所要做的只是统一变量与引用的处理。也就是说,所有变量都可以被视为引用,而不仅仅是标量。在这种情况下,下标总是解除数组或哈希名称左侧引用的隐式引用。
然而,这有两个主要含义。这意味着Perl程序员必须学会在原本写$foo[1]
的地方写@foo[1]
。我认为大多数Perl 5程序员会适应这一点,因为他们中很多人最初觉得当前的语法有点奇怪。
第二个含义是切片需要一个新符号,因为下标不再由初始的奇怪字符控制其标量/列表上下文。相反,下标的上下文需要由以下组合之一来控制:
整个表达式的上下文。
下标中已知列表运算符的出现,如逗号或范围。
显式地将下标内部转换为列表或标量上下文的语法。
显式声明默认行为。
可能不应该涉及的是数组对象在运行时的类型,因为如果可能的话,上下文真的需要在编译时计算。
无论如何,可能会有人希望下标默认为标量,而其他人则希望它们默认为列表。根据你更倾向于APL程序员还是普通人,这两个默认值都有很好的论据。
还有其他更广泛的影响。如果将复合变量视为标量引用,那么@foo
和%foo
的名称实际上是标量变量,除非显式解引用。这意味着在标量上下文中提到它们时,你得到Perl 5的\@foo
和\%foo
的等价物。这极大地简化了原型系统,因为像push
这样的运算符不再需要为其第一个参数指定某种特殊的引用上下文 - 它只需要指定一个标量上下文,这就足够假设其第一个参数生成引用。(当然,如果函数签名想要更具体,总是可以这样做的。关于这一点,将在未来的部分中详细介绍。)
对于赋值运算符也有影响,因为它必须能够在不意外地调用列表上下文并复制列表而不是列表引用的情况下将数组引用赋给数组变量。我们可以发明另一个赋值运算符来区分这两种情况,但到目前为止,裸变量和切片看起来将像Perl 5中那样作为lvalue行为,而括号中的列表将改变为更接近Perl 6在函数调用中绑定形式参数到实际参数的方式的右手参数绑定。也就是说,
@foo = (1,2,3);
将为右侧提供一个无界的列表上下文,但
(@foo, @bar) = (@bar, @foo)
将为右侧提供一个请求两个标量值的上下文,这些值是数组引用。这将是无标记lvalue列表的默认值,但将有一种简单的方法来标记形式数组参数和散列参数以用列表上下文“吞噬”其余参数,就像它们在Perl 5中默认做的那样。
(或者,我们可能会保留带有Perl 5语义的普通列表赋值运算符,并定义一个新的赋值运算符,如:=
,它执行签名赋值。我可以在两个方向上为这个论点辩护。)
正如数组和哈希可以通过下标引用(或在列表上下文中隐式引用),函数也是通过名称而非&foo
来调用,并且通过括号(或作为不带 ampersand 的裸名称)显式引用(或两者均可)。Perl 5 中 ampersand 的意义不再有效,因为 ampersand 将不再意味着签名匹配被抑制——将有一个不同的机制来处理这个问题。由于没有括号的 &foo
不会进行调用,因此现在不再可能使用该语法自动传递 @_
数组——您现在必须显式地使用 foo(@_)
来完成。
标量变量是特殊的,因为它们可以包含引用或实际的“原生”值,并且没有像其他类型那样的特殊解引用语法。Perl 6 将尽可能地隐藏这种区别。也就是说,如果 $foo
包含原生整数,调用 $foo.bar
方法将在内置类型上调用方法。但如果 $foo
包含对某个其他对象的引用,它将在该对象上调用方法。这与我们在 Perl 5 中关于重载的想法一致,因此您不应该对此行为感到惊讶。在这种情况下,可能需要特殊的语法来获取引用变量本身的方法,但如果特殊情况是特殊的,那就没关系。
RFC 133:变量名称的替代语法
这个 RFC 有一个合理的观点,但实际上我们将做与它相反的事情。也就是说,我们将把奇怪的字符视为名称的一部分,并使用下标作为上下文。这样更好,因为只有一个奇怪的字符,但有很多可能的解引用形式。
RFC 134:数组和哈希的替代切片
我们将肯定取消 Perl 5 的切片语法,至少是在依赖于初始字符来确定下标上下文的情况下。我们可以以多种方式重新引入切片语法,其中一些在 RFC 中提到,但我们将在数据结构第 9 次末日讨论时决定此事,因为设计切片语法的有趣部分将由对切片多维数组的需要所驱动。
目前我们只能说,数组可以具有与函数参数签名类似的下标签名。普通的一维数组(和哈希)可以支持某种类型的简单切片语法,这可以扩展到更复杂的数组,同时允许多维数组以更有利于数值编程的方式区分简单切片和将列表和函数映射到下标的复杂映射。
关于哈希切片返回成对而不是值的问题,我们可以用特殊的切片语法来区分这一点,或者我们可以建立哈希列表上下文的观念,告诉切片返回成对而不是仅返回值。(如果可能在不使用特殊切片语法的情况下在成对列表和普通列表之间进行类型转换,我们可能不需要特殊的切片语法。)
RFC 196:哈希的更直接语法
这个 RFC 提出了三个建议,我们将分别考虑。
建议 1 是“在标量上下文中,哈希评估为哈希中的键的数量。”(您现在可以找出这一点,但只能通过在标量上下文中使用 keys()
函数。)如果我们将“标量上下文”改为“数字上下文”,那么建议 1 是可以接受的,因为在标量上下文中,哈希将产生一个指向哈希的引用,这个引用恰好可以转换为条目数。
我们还必须认识到,一些哈希的实现可能需要遍历并计数所有条目才能返回实际数量。幸运的是,在布尔上下文中,找到单个条目就足以确定哈希是否包含内容。然而,在那些不跟踪条目数量的哈希中,找到任何条目都可能重置哈希上的任何活动迭代器,因为一些哈希的实现(尤其是那些不跟踪条目数量的实现)可能只能提供一个迭代器。
提案2是“通过显式调用reset()
函数来重置哈希中的迭代器。”这是可以的,但前提条件是它不是一个函数,而是一个方法,属于HASH类。
提案3实际上是关于sort
识别成对元素并执行正确操作的问题。默认按照$^a[0] cmp $^b[0]
排序可能是合理的,这也是成对键值所在的位置。然而,正确的解决方案可能是简单地为匿名列表提供一个默认字符串方法,当cmp
请求其任一参数的字符串表示时,该方法可以产生一个不错的键值来排序。sort
本身可能只需集中缓存返回的字符串,以免需要重新计算。
RFC 201:哈希切片
本RFC建议在索引中使用%
作为特殊哈希切片的标记。遗憾的是,由于所有哈希引用都以%
开头,这个有趣的字符将无法用于此目的。简洁的列表推导式需要在索引中使用其他语法,这有望推广到数组。
关于变量的其他决策
Perl 6中去除了各种特殊标点符号变量,包括所有已弃用的变量。(非弃用变量将被某些类似的功能替换,这些功能可能通过在适当对象上调用某种方法来调用。如果没有适当的对象,则可能通过名为的全局变量提供类似的功能。)
释放各种括号字符使我们能够将它们用于其他目的,例如表达式的插值。
"$(expr)" # interpolate a scalar expression
"@(expr)" # interpolate a list expression
$#foo
已消失。如果你想获取数组的最后一个索引,而[-1]
不够好,请使用@foo.end
代替。
其他特殊变量(如正则表达式变量)将从动态作用域变为词法作用域。在Perl 6中,甚至$_
和@_
可能也将是词法作用域。
名称
在Perl 5中,词法作用域是无名和不可命名的。在Perl 6中,当前词法作用域将有一个名称,该名称在词法作用域内作为伪类MY
可见,这样它就可以(如果愿意的话)在编译时将词法作用域的管理委托给其他模块。用正常的话说,这意味着当你使用一个模块时,你可以让它以词法方式以及包方式导入内容。
类型全局变量已消失。相反,你可以通过类似于Perl 5的结构化符号表哈希来获取变量对象。变量对象$MyPackage::foo
存储在
%MyPackage::{'$foo'}
请注意,这个有趣的字符是名称的一部分。Perl中不再有任何结构将与名称“foo
”相关联。
Perl的特殊全局名称存储在名为“*
”的专用包中,因为它们逻辑上存在于所有不隐藏它们的词法作用域中。因此,标准输入文件句柄的无歧义名称是$*STDIN
,但一个包可以仅引用$STDIN
,如果未声明具有该名称的包或词法变量,则它将默认为$*STDIN
。
一些这些特殊变量实际上可能会在每个词法作用域或每个线程中进行克隆,所以仅仅因为一个名称在特殊的全局符号表中,并不意味着它总是在所有模块中表现出全局行为。特别是,影响解析器工作方式的符号表更改必须是词法作用域的。仅仅因为我为我的酷炫新超引用结构安装了一个特殊规则,并不意味着其他人必须忍受它。在最极端的情况下,仅仅因为我安装了Python解析器,它也不应该强迫其他模块进入一个扭曲的小空格迷宫。
另一种看待它的方法是,“*
”包中的所有名称都自动导出到每个包和/或外部词法作用域。
文字
数字文字中的下划线
允许在数字中的任意两个数字之间使用下划线。
RFC 105:删除“字符串中的@必须为\@”错误
好的。
RFC 111:Here Docs终止符(曾是空白和Here Docs)
好的。
RFC 162:Heredoc内容
我认为我最喜欢选项(e):删除等于终止符的空格。
默认情况下,如果需要做出猜测,它应该假设硬制表符是8个空格宽。这通常不会引起问题,因为大多数情况下制表符将始终一致,而且不需要猜测。这把责任放在了使用非标准制表符的人身上,确保它们是一致的,这样Perl就不必猜测。
用户定义的运算符可以轻松地完成任何额外的修改。
RFC 139:允许使用类似s///的语法调用任何函数
创意引用将被允许,但我们在同一时间不能以两种不同的方式解析foo(bar)
,而且我不愿意阻止人们使用括号作为引用字符。我看不出我们如何合理地有新的引用运算符而没有显式声明。如果引用类似运算符的效用足够大,那么要求这样的声明应该几乎没有相对负担。
这种声明的形式留待读者作为函数属性定义的练习。我们可能在本系列中的稍后部分重新审视这个问题。也有可能像qx//
这样的引用运算符可以有一个相应的函数名,如quote:qx
,可以作为一个函数调用。
RFC 222:对象方法调用插入
我已经渴望方法进行插入很长时间了,所以我支持这个RFC。随着我们鼓励人们使用访问器方法来引用类本身之外的属性,这将成为双倍重要。
然而,我有一个“但是”。由于我们将使用.
而不是->
,我认为为了理智起见,我们可能需要要求括号,否则“$file.$ext
”会给人们带来麻烦。更不用说“$file.ext
”了。
RFC 226:单引号上下文中的选择性插入。
这个建议有很多优点,但也有困难,我几乎直接拒绝它,仅仅因为Perl 5的单引号策略是成功的。我认为这个RFC中关于\I
…\E
的建议很丑。(而且我想废除\E
,并使用括号作用域。)
然而,我认为我们可以通过将单引号中的插入视为困难而不是容易解决的问题来克服一个主要的“无法从这里到达那里”的问题。基本问题是太容易遇到一个\$
或\@
(或者一个\I
),它想要被当作字面量处理。我认为我们可以允许将任意表达式插入到单引号字符串中,但仅限于一个不太可能的序列,其中需要三个或更多字符才能识别。最有效的心理模型似乎是将一种引用嵌入到另一种引用中,所以我认为这个
\q{stuff}
将嵌入单引号内容,而这个
\qq{stuff}
可以嵌入双引号的内容。然后可以通过这种方式将变量插入单引号字符串中:
\qq{$foo}
RFC 237:哈希应在双引号字符串中进行插值
原则上我同意这个RFC,但我们不能使用即将在Perl 6中消失的变量来定义默认的哈希字符串化函数,所以RFC中提出使用$"
是错误的。
所有对象都应该有一种方法来生成可读的输出。用户偏好如何覆盖这一点尚有争议。当然,动态作用域有其问题。但是,对象偏好的词法覆盖也成问题。单独的对象属性似乎为解决这个问题提供了一个合理的方式。下面会进一步讨论。
关于printf
格式,我看不到任何方法可以使%d
不是一个数组,所以我们将不得不在一般情况下将格式放入单引号中。那些同时插入变量的格式字符串将能够使用新的\qq{$var}
功能。
对于那些认为我们应该只坚持Perl 5插值规则的人:我们必须允许%
现在引入插值,因为单个哈希值不再使用$foo{$bar}
命名,而是%foo{$bar}
。因此,我们最好允许完整哈希的插值。
RFC 251:类方法调用的插值
类方法调用相对较少(除了构造函数,它们很少会进行插值)。所以,我们不应该扫描可能引入类的标识符,而是应该依赖于表达式插值。
"There are $(Dog.numdogs) dogs."
RFC 252:子程序插值
我认为子程序应该进行插值,前提是它们以有趣字符引入。(另一方面,$(sunset $date)
或@(sunset $date)
有多难?从另一个角度看,我喜欢&
与$
、@
和%
的一致性。)
我认为需要括号,因为在Perl 6中,标量&sub
将只返回一个引用,如果你真的想解引用子程序引用,则需要括号。(确实,当用作列表运算符时,子程序可以不使用括号进行调用,但你不能在没有有趣字符的情况下插值。)
对于那些担心使用&
进行签名检查抑制的人,我们应该指出,在Perl 6中,&
将不再是抑制签名检查的方法,所以这并不重要。
RFC 327:\ v
用于垂直制表符
我认为不保留\v
以供未来使用的代价太高,以至于无法证明保留与几乎不再使用的功能兼容性的小效用。例如,我几乎要使用\v
和\V
来切换到和退出文字(单引号)模式,直到我决定将它们与引号语法统一,并使用\qq{}
和\q{}
。
RFC 328:单引号不插值\’和\
我认为通过声明你的引号规则,超引号将是可能的,所以我们将不会更改基本单引号规则(除了支持\q
)。
关于文字的其他决策
\L等的作用域
我想取消使用\E
作为作用域结束标记的无聊做法。相反,如果任何序列如\L
、\U
或\Q
希望施加作用域,那么它必须使用大括号包围该作用域:\L{
内容}
、\U{
内容}
或\Q{
内容}
。在内容中包含的任何字面大括号都必须转义。(作为语法的大括号(如用于索引)应该正确嵌套。)
裸词策略
Perl 6中将没有裸词。任何已声明的包名称的裸名称将被解释为字符串化到包名称的类对象。所有其他裸名称将被解释为子程序或方法调用。对于非严格应用程序,未定义的子程序将自动定义自身以返回其名称。注意,在${name}
等中,名称被视为自动引用,而不是裸词。
奇怪的括号
使用括号进行区分
"${foo[bar]}"
从
"${foo}[bar]"
将不再被支持。取而代之,表达式解析器将尽可能多地获取,您可以通过插入一个由\Q
指定的空字符串,在任何特定点让它停止。
"$foo\Q[bar]"
特殊令牌
特殊令牌在MY
伪包下将转换为POD指令或词法作用域的OO方法。
Old New
--- ---
__LINE__ MY.line
__FILE__ MY.file
__PACKAGE__ MY.package
__END__ =begin END (or remove)
__DATA__ =begin DATA
Heredoc语法
我认为heredocs将要求任何标识符周围有引号,并且我们确保支持<< qq(END)
风格的引号。现在在(必需的)引号令牌之前允许有空格。请注意,现在可以定义自定义引号,因此如果您为您的花哨超引用算法定义了一个花哨的qh
运算符,那么您可以这样说<<qh(END)
。
仍然可以使用<<""
来获取到下一个空白行之前的所有内容。然而,Perl 6将认为只包含空格、制表符等的任何行都是空白行,而不仅仅是那些直接以换行符终止的行。
上下文
在Perl 5中,大部分上下文处理都是在运行时完成的,即便如此,一个特定的函数也只能发现它是在void、标量还是列表上下文中。在Perl 6中,我们将扩展上下文的概念,使其更适合编译时和运行时分析。特别是,一个函数或方法可以知道(理论上甚至在编译时)它是在被调用在
Void context
Scalar context
Boolean context
Integer context
Numeric context
String context
Object context
List context
Flattening list context (true list context).
Non-flattening list context (list of scalars/objects)
Lazy list context (list of closures)
Hash list context (list of pairs)
(此列表可能并不完整。)
这些上下文(除了void之外)都对应于声明函数(或列表赋值的左侧)参数的方式,以向实际参数列表(或列表赋值的右侧)提供上下文。默认情况下,参数将提供对象上下文,这意味着单个参数期望是实际参数的别名,即使数组和哈希也不做列表上下文,除非您明确声明它们。这些还没有确定下来(甚至不是果冻),但这里有一些对应于那些上下的可能参数声明想法
Scalar context
Boolean context bit $arg
Integer context int $arg
Numeric context num $arg
String context str $arg
Object context $scalar, %hash, Dog @canines, &foo
List context
Flattening list context *@args
Non-flattening list context $@args
Lazy list context &@args
Hash list context *%args
(我还预计一元*将强制在rvalue上下文中压平数组。这是我们如何克服Perl 6中的类型签名,而不是依赖于初始的&符号。所以,您可以说push *@list
而不是Perl 5的&push(@list)
,并且它不会关心push
的参数签名是什么。)
也可以定义属性来修改形式参数,尽管这可能会很快变得笨拙,并且我希望能有一种简洁的语法来处理常见情况,例如最后一个参数以传统方式吞噬列表。因此,内置的push
签名可能是
sub push (@array, *@pushees);
实际上,签名可能只是(*@pushees)
,如果push
确实是ARRAY
类中的方法,并且对象是隐式传递的
class ARRAY;
sub .push (*@pushees);
sub .pop (;int $numtopop);
sub .splice (int $offset, int $len, *@repl);
但我们跑在前面了。
顺便说一句,所有函数和方法的参数(除了对象本身)除非声明为具有rw
属性,否则都将被认为是只读的。(列表赋值将默认为另一种方式。)这将防止当前Perl实现必须经历的许多无谓的运动,以确保所有函数参数都是有效的lvalues,而实际上其中大部分根本未修改。
嗯,我们还在前面跑。回到上下文。
现在引用对布尔上下文是透明的
在Perl 6中,引用不再被认为是“始终为真”。任何类型都可以重载其bit()
类型转换操作符,任何没有自己的bit()
的类型都从别处继承一个,至少从类UNIVERSAL。内置的位方法具有内置类型的预期布尔语义,因此数组在它们有内容时为真,字符串在它们不是""
或"0"
时为真等。
列表
RFC 175:添加list
关键字以强制列表上下文(类似于scalar
)
又一篇被从垃圾堆中挽救的RFC。在Perl 6中,类型名称将一般标识类型转换函数。(类型转换函数只是强制上下文——除非实际上下文不同,否则它不执行任何操作。)在Perl 6中,用于标量上下文中的列表将自动将其自身转换为列表的引用,而不是返回最后一个元素。(如果确实需要,可以使用下标[-1]
来显式获取最后一个元素。但在实际应用中,这种情况很少见。)因此,显式列表构造器
[1,2,3]
是以下类似结构的语法糖
scalar(list(1,2,3));
具体取决于我们是否继续强调列表/数组的区别,它可能实际上会被写成
scalar(array(1,2,3));
其他类型转换可能用到的词汇有hash
(提供pairlist上下文)和objlist
(为表达式列表提供标量上下文)。也许甚至可以考虑将可选的sub
关键字视为对后续可能不视为闭包的块的类型转换。也许sub
实际上应该被写成lazy
。在这种情况下,我们甚至可能有一个lazylist
上下文,为表达式列表提供懒上下文。
当然,当您想在编译时明确指定这些上下文时,可以使用标准类型转换,如int()
、num()
和str()
。(Perl 5已经有了这些上下文,但仅限于运行时。)注意,由于一元函数和方法之间的关系,$foo.int
、$foo.num
和$foo.str
将是相同类型转换的不同写法。
不用担心您的代码将充满类型转换,我应该指出,您不太可能经常使用这些类型转换,因为通常每个上下文都将由您调用的函数或方法的签名隐含地表示。(Perl仍然会在有意义的时候为您自动转换。)更多关于这一点的内容将在第6次启示录中讨论,如果还没有的话。
因此,尽管布尔上下文可以通过以下方式显式指定
if (bit $foo)
或
if ($foo.bit)
但在Perl 5中,您通常只需像这样写
if ($foo)
关于列表的其他决策
基于我们之前所说的一些内容,您可以看到我们将能够定义各种类型的懒生成列表。然而,这些操作符的具体设计留给后续的启示录。在这里,我想指出,我认为关于数组下标生成的一些提议应该被推广到下标以外的使用。这可能会对在期望运算符的地方使用:
字符施加一些限制,例如。
如上所述,我们将有多种不同的列表上下文。特别是,将有一个哈希列表上下文,它假设您正在向它提供成对的值,如果您没有提供成对的值,它将假设您提供的值是键,并提供一个默认值。可能存在将哈希默认设置为有趣值(如0或1)的方法。
为了做到这一点,=>
运算符至少需要将它的左操作数标记为键。更有可能的是,它实际上在Perl 6中创建了一个成对的对象。并且{ foo => $bar }
列表构造器将需要使用=>
(或在哈希列表上下文中),否则它将被解释为没有sub
的闭包。(您始终可以使用显式的sub
或hash
来转换方括号到正确的解释。)
我注意到许多程序到处都使用qw()
(比输入运算符更频繁),我一直觉得qw()
有些丑陋,所以我想用更漂亮的东西来替换它。由于输入运算符正在使用一对完美的括号字符来获得微小的语法收益,我们将偷走这些字符,并将它们变成类似qw的列表构造器。在普通列表上下文中,以下内容将是相同的
@list = < foo $bar %baz blurch($x) >;
@list = qw/ foo $bar %baz blurch($x) /; # same as this
@list = ('foo', '$bar', '%baz', 'blurch($x)'); # same as this
但在哈希列表上下文中,它可能相当于以下内容
%list = < foo $bar %baz blurch($x) >;
%list = (foo => 1, '$bar' => 1, '%baz' = 1, blurch => $x); # same as this
文件
基本上,文件句柄只是可以作为迭代器使用的对象,而且不再属于本章的内容。
RFC 034:不应使用尖括号进行文件通配符匹配
确实如此,它们将不再使用。实际上,我怀疑根本不会使用尖括号进行输入。请参见以下内容。呃,上方。
RFC 051:尖括号应接受文件名和列表
在Perl 6中可能不需要显式的输入运算符,我想要使用尖括号来表示其他内容。I/O句柄是迭代器的一个子类,我认为通用迭代器变量将能够代替输入运算符的功能,尤其是在它们可以在上下文中执行正确操作的情况下。例如,要从标准输入读取,只需说
while ($STDIN) { ... }
迭代器就会知道应该将值赋给$_
,因为它处于布尔上下文中。
我更倾向于将这个RFC看作是请求一种通用方法来根据迭代器的类型初始化迭代器。这个问题的关键是防止每次重新评估规范——例如,你不想每次从文件中读取一行时都重新打开文件。在Perl 6中,将有标准的方式来抑制评估,无论是从调用者的角度还是被调用者的角度。无论如何,模型是传递一个匿名子程序,仅在适当的时候调用它。因此,迭代器语法可能将它的参数原型化为匿名子程序,或者用户可能显式地传递一个匿名子程序,或者两者都传递。无论如何,在Perl 6中,sub
关键字将是可选的,所以像
while (file {LIST}) { ... }
这样的代码可以使得将评估延迟到适当的时间(如果列表是即时生成的,则可能有多个时间点)。对于适当的参数声明,我想甚至可以省略括号。
属性
各种类型的变量和值都有各种与它们类型自然相关联的数据属性。你知道狗出生时就有摇尾巴的能力,希望尾巴是连接的。这只是狗性的一部分。
然而,很多时候,你想要类似便利贴的功能,这样你就可以暂时将一些任意信息附加到某个设备上,虽然它不是为此设计的,但确实是放置便利贴的正确位置。同样,Perl 6中的变量和值允许你附加称为“属性”的任意信息。本质上,Perl中的任何对象都可以有一个包含这些属性的关联散列,这些属性通过散列键命名。
其中一些属性在编译时是已知的,实际上不需要与相关对象存储在一起,而是可以存储在变量的符号表条目中。因此,编译时属性可以附加到任何类型的变量上。
运行时属性实际上是与相关对象相关联的,这暗示了一些开销。因此,像int
和num
这样的固有数据类型可能允许或不允许运行时属性。在允许的情况下,固有类型通常必须提升为其相应的对象类型(或者包裹在一个将实际值委托回原始固有类型的对象中)。但你肯定不想因为想要在某个位上贴便条而将一个包含一百万位的数组提升为一个包含一百万个对象的数组,所以在这种情况下,很可能不允许这样做,或者位可能被克隆而不是引用,或者类似的事情。
属性也可以附加到子程序上。
通常情况下,您不会直接设置或清除属性 - 而是调用一个访问器方法来完成这项工作。如果没有该名称的方法,Perl 会假设有一个名称相同的方法,它只是设置了或清除了属性。然而,使用访问器方法来设置或清除属性,我们可以定义合成属性。例如,可能有一个真实的 constant
属性,您可以将其附加到一个变量上。某些变量(如函数原型中的变量)可能会默认设置为 constant
。在这种情况下,设置一个合成属性,如 rw
,可能会清除底层 constant
属性。
属性可以通过“is”关键字附加到上述表达式中。以下是在变量上设置的编译时属性:
my int $pi is constant = 3;
以下是在返回值上设置的运行时属性:
return 0 is true;
属性是在编译时应用于变量还是在运行时应用于值,取决于它是在 lvalue 还是 rvalue 上下文中。(即使您没有对它们进行赋值,变量声明也总是在 lvalue 上下文中。)
“is”的工作方式与方法调用中的“.”类似,只不过返回值是左侧的对象,而不是方法的返回值,后者被丢弃。
实际上,在某些情况下,“is”是可选的。因此,您可能会看到类似以下内容:
my int $pi constant = 3;
return 0 true;
在这种情况下,这些方法实际上被解析为后缀运算符。(然而,我们可能会将其作为严格规则,只有对于预定义的属性方法,您才能省略“is”。)
由于这些实际上是方法调用,因此您可以传递除问题对象外的参数。
my int @table is dim(366,24,60);
我们上面的示例假设一个参数为 (1)
。
my int $pi is constant(1) = 3;
return 0 is true(1);
由于在常见情况下“is”是可选的,因此您可以在不重复“is”的情况下堆叠多个属性。
my int $pi is shared locked constant optimize($optlevel) = 3;
(请注意,这些方法是在编译时对 $pi
变量进行调用的,因此您需要确保您调用的所有内容都已定义。例如,$optlevel
需要在编译时已知。)
以下是从 Damian 那里偷来的一些属性想法。(我想这算是知识产权盗窃。)其中一些名称已被更改以保护无辜者。
# Subroutine attributes...
sub name is rw { ... } # was lvalue
my sub rank is same { ... } # was memoized
$snum = sub is optimize(1) { ... }; # "is" required here
# Variable attributes...
our $age is constant = 21; # was const
my %stats is private;
my int @table is dim(366,24,60);
$arrayref = [1..1000000] is computed Purpose('demo of anon var attrs');
sub choose_rand (@list is lazy) { return $list[rand @list] }
# &@list notation is likely
$self = $class.bless( {name=>$name, age=>$age} is Initialized );
# Reference attributes...
$circular = \$head is weak;
# Literal attributes...
$name = "Damian" is Note("test data only");
$iohandle = open $filename is dis(qw/para crlf uni/) or die;
$default = 42 is Meaning(<<OfLife);
The Answer
OfLife
package Pet is interface;
class Dog inherits('Canine') { ... }
print $data{key is NoteToSelf('gotta get a better name for this key')};
(我不赞同将属性用于所有这些事情,但它确实很令人惊讶,您可以将它推得多远。)
属性名称应以下标识别符(包括 Unicode 字母和象形文字)开头。如果有的话,参数的解析由相关方法的签名控制。没有“.”的属性方法调用总是修改其底层属性。
如果作为普通方法(带有“.”)调用,则属性值将返回而不会被修改。该值然后可以被运行时属性修改。例如,$pi.constant
会返回 1
而不是 $pi
的值,所以我们得到:
return $pi.constant is false; # "1 but false" (not possible in Perl 5)
另一方面,如果您省略了点,会发生其他事情
return $pi constant is false; # 3 but false (and 3 is now very constant)
以下是一些更多经过处理的 Damian 示例
if (&name.rw) { ... }
$age++ unless $age.constant;
$elements = return reduce $^ * $^, *@table.dim;
last if ${self}.Initialized;
print "$arrayref.Purpose() is not $default.Meaning()\n";
print %{$self.X}; # print hash referred to by X attribute of $self
print %{$self}.X; # print X attribute of hash referred to by $self
print %$self.X; # print X attribute of hash referred to by $self
与无点形式一样,如果没有实际对应于属性的属性,Perl 会假装有一个基本的返回实际属性的属性。
由于这些方法返回属性(除非由无点语法覆盖),您可以像任何方法一样临时存储属性,只要该方法本身允许写入。
temp $self.X = 0;
请注意
temp $self is X = 0;
将赋值 0 给 $self
。 (是否在 $self
变量上在运行时设置编译时的 X 属性实际上是否有意义,任何人都可以猜测。)
请注意,由于它们的语法,属性不能通过字符串插值来设置。所以,很高兴
print "My $variable is foobar\n";
不会尝试设置 $variable
上的 foobar
属性。
关键字“is
”的绑定优先级与“。
”相同,即使它实际上并不存在。
注意,当你说$foo.bar
时,如果你有一个编译时的属性(这在编译时已知,废话),你会得到$foo
的编译时属性。否则,它是一个普通的方法调用(它只会在找不到方法时在运行时查找属性,所以它不应该影响普通方法调用的开销。)
要直接获取属性而不通过方法接口,请使用特殊的btw
方法,它返回属性哈希的引用。
$foo.btw{constant}
注意,合成属性不会出现在那里!
本启示录中的所有属性名称都不应被视为最终确定。我们将随着系列的进行确定实际的属性名称。
嗯,这就是启示录2的内容了。毫无疑问,这里有一些我应该决定但还没决定的事情,但至少我们在进步。嗯,至少我们在朝某个方向发展。现在是时候跳启示录舞曲,向Jon Orwant和他的新妻子致敬了。
标签
反馈
这篇文章有什么问题吗?请通过在GitHub上打开一个问题或拉取请求来帮助我们。