我的 class 和我们在 Raku(perl6) 中的 class 有什么区别?
What is the difference between my class and our class in Raku(perl6)?
我已经阅读了规范,但我仍然对 my class
与 [our] class
的区别感到困惑。有什么区别以及何时使用哪个?
与变量一样,my
在词法上绑定一个名称,而 our
在周围的包中另外创建一个条目。
module M {
our class Foo {}
class Bar {} # same as above, really
my class Baz {}
}
say M::Foo; # ok
say M::Bar; # still ok
say M::Baz; # BOOM!
将 my
用于模块内部的 类。当然,您仍然可以通过标记 is export
.
使这些本地符号可用于导入代码
my
与 our
的区别主要与生成符号 table 相关。例如:
my $a; # Create symbol <$a> at top level
package Foo { # Create symbol <Foo> at top level
my $b; # Create symbol <$b> in Foo scope
our $c; # Create symbol <$c> in Foo scope
} # and <Foo::<$c>> at top level
在实践中,这意味着任何 our
范围内的内容都可以通过在包标识符前加上前缀($Foo::c
或 Foo::<$c>
是同义词)轻松地与外界共享,以及任何is my
scoped 不是现成可用的——尽管您当然可以通过例如 getter subs.
提供对它的访问
大多数时候您会想要使用 my
。大多数变量只属于它们当前的范围,没有人有任何业务高峰。但是 our
在某些情况下可能有用:
- 不会毒化符号 table 的常量(这就是为什么实际上使用
constant
意味着 一个 our
作用域) .因此,您可以通过使用 package Colors { constant red = 1; constant blue = 2; }
然后将它们引用为 Colors::red
来制作更 C 风格的 enum/constants
- 类 或 subs 应该可以访问但不需要导出(或者不应该因为与内置或其他模块重叠的符号)。导出符号可能很棒,但有时使用 package/module 命名空间来提醒您需要什么东西也很好。因此,它也是在运行时通过 subs 管理选项的好方法:
CoolModule::set-preferences( ... )
。 (虽然这里也可以使用动态变量来达到很好的效果)。
我相信其他人会在其他时候评论 our
范围很有用,但这些是我自己的经验。
my
作用域声明符暗示了词法作用域:在其声明之后,该符号对当前大括号集中的代码可见。因此,我们倾向于将一对大括号内的区域称为“词法范围”。例如:
sub foo($p) {
# say $var; # Would be a compile time error, it's not declared yet
my $var = 1;
if $p {
$var += 41; # Inner scope, $var is visible
}
return $var; # Same scope that it was declared in, $var is visible
}
# say $var; # $var is no longer available, the scope ended
由于变量的可见性与其在代码中的位置直接相关,因此词法作用域对于能够对程序进行推理非常有帮助。这适用于:
- 程序员(既是为了他们自己对程序的推理,也是因为当事物具有词法作用域时可以检测和报告更多错误)
- 编译器(词法范围允许更容易和更好的优化)
- 诸如 IDE 之类的工具(对具有词法范围的事物进行分析和推理要容易得多)
在后来成为 Raku 的语言的早期设计过程中,子例程没有默认具有词法作用域(并且具有 our
作用域,就像在 Perl 中一样),但是人们意识到词法作用域是一个更好的默认值。进行子例程调用总是尝试解析具有词法作用域的符号,这意味着有可能在编译时报告未声明的子例程。此外,词法范围内的符号集在编译时是固定的,并且在声明性结构(如子例程)的情况下,例程以只读方式绑定到该符号。这也允许诸如多重分派的编译时解析、编译时参数检查等事情。 Raku 语言的未来版本很可能会指定越来越多的对词法作用域程序元素的编译时检查。
既然词法作用域这么好,为什么还要存在 our
(也称为包)作用域?简而言之,因为:
- 有时我们想要比在给定的词汇范围内更广泛地共享事物。我们可以只声明所有词法,然后标记我们想要与
is export
共享的东西,但是..
- 一旦我们到了使用大量不同库的地步,让所有东西都试图将事物导出到消费者的单一词法范围内可能会导致很多冲突
包允许符号命名空间。例如,如果我想在同一代码中为 HTTP 和 WebSockets 使用 Cro 客户端,我可以愉快地使用两者,并将它们分别称为 Cro::HTTP::Client
和 Cro::WebSocket::Client
。
包由 包声明符 引入,例如 class
、module
、grammar
和(有警告)role
. our
声明将在封闭的包结构中进行安装。
这些包最终存在于名为 GLOBAL
的顶级包中 - 这很合适,因为它们在全球范围内有效可见。如果我们声明一个 our
-scoped 变量,那么它就是一个全局变量(尽管希望是一个名称 spaced 的变量),关于它的文章已经写得足够多了,我们知道我们应该停下来思考一下,想知道是否全局变量是最好的 API 决定(因为最终,通过 GLOBAL
可见的所有内容都是 API 决定)。
然而,做 有点模糊的地方是我们可以词法包。这些是 GLOBAL
中未安装的软件包。我发现这些在进行 OO 编程时非常有用。例如,我可能有:
# This class that ends up in GLOBAL...
class Cro::HTTP::Client {
# Lexically scoped classes, which are marked `my` and thus hidden
# implementation details. This means I can refactor them however I
# want, and never have to worry about downstream fallout!
my class HTTP1Pipeline {
# Implementation...
}
my class HTTP2Pipeline {
# Implementation...
}
# Implementation...
}
词法包也可以嵌套并包含 our
范围内的变量,但最终不会全局可见(除非我们选择以某种方式将它们泄露出去)。
不同的 Raku 程序元素已被赋予默认范围:
- 子例程默认为词法 (
my
) 范围
- 方法默认为
has
范围(仅通过方法分派可见)
- 类型(class、角色、语法、子集)和模块声明默认为包(
our
)范围
- 常量和枚举默认为包 (
our
) 作用域
实际上,最常共享的东西默认在包范围内,其余的则没有。 (变量确实迫使我们明确选择一个范围,但最常见的选择也是最短的输入范围。)
就我个人而言,我犹豫要不要让某个东西比语言默认设置更显眼,但是我通常会让它们不那么显眼(例如,my
用于内部使用的常量,以及用于构建实现细节的 classes)。当我可以通过在全局可见的包中公开一个 our
范围的变量来做某事时,我仍然经常喜欢让它成为 my
范围并提供一个 sub
(导出)或method
(由于在包范围 class
上可见)来控制对它的访问,以便在将来为自己购买一些灵活性。我认为现在做出错误的选择是可以的,如果我给自己 space 让他们在未来变得更正确而不给任何人带来不便。 :-)
总结:
- 对所有实施细节使用
my
范围
- 也对您计划
export
的事物使用 my
范围,但请记住,导出会将符号放入消费者的单一词法范围内,并且存在名称冲突的风险,因此在导出特别通用的内容时要多加考虑名字
- 将
our
用于共享的内容,以及需要使用命名空间以避免冲突的时候
- 无论如何,我们最想共享的元素默认为
our
范围,因此显式编写 our
应该让我们停下来思考一下
我已经阅读了规范,但我仍然对 my class
与 [our] class
的区别感到困惑。有什么区别以及何时使用哪个?
与变量一样,my
在词法上绑定一个名称,而 our
在周围的包中另外创建一个条目。
module M {
our class Foo {}
class Bar {} # same as above, really
my class Baz {}
}
say M::Foo; # ok
say M::Bar; # still ok
say M::Baz; # BOOM!
将 my
用于模块内部的 类。当然,您仍然可以通过标记 is export
.
my
与 our
的区别主要与生成符号 table 相关。例如:
my $a; # Create symbol <$a> at top level
package Foo { # Create symbol <Foo> at top level
my $b; # Create symbol <$b> in Foo scope
our $c; # Create symbol <$c> in Foo scope
} # and <Foo::<$c>> at top level
在实践中,这意味着任何 our
范围内的内容都可以通过在包标识符前加上前缀($Foo::c
或 Foo::<$c>
是同义词)轻松地与外界共享,以及任何is my
scoped 不是现成可用的——尽管您当然可以通过例如 getter subs.
大多数时候您会想要使用 my
。大多数变量只属于它们当前的范围,没有人有任何业务高峰。但是 our
在某些情况下可能有用:
- 不会毒化符号 table 的常量(这就是为什么实际上使用
constant
意味着 一个our
作用域) .因此,您可以通过使用package Colors { constant red = 1; constant blue = 2; }
然后将它们引用为Colors::red
来制作更 C 风格的 enum/constants
- 类 或 subs 应该可以访问但不需要导出(或者不应该因为与内置或其他模块重叠的符号)。导出符号可能很棒,但有时使用 package/module 命名空间来提醒您需要什么东西也很好。因此,它也是在运行时通过 subs 管理选项的好方法:
CoolModule::set-preferences( ... )
。 (虽然这里也可以使用动态变量来达到很好的效果)。
我相信其他人会在其他时候评论 our
范围很有用,但这些是我自己的经验。
my
作用域声明符暗示了词法作用域:在其声明之后,该符号对当前大括号集中的代码可见。因此,我们倾向于将一对大括号内的区域称为“词法范围”。例如:
sub foo($p) {
# say $var; # Would be a compile time error, it's not declared yet
my $var = 1;
if $p {
$var += 41; # Inner scope, $var is visible
}
return $var; # Same scope that it was declared in, $var is visible
}
# say $var; # $var is no longer available, the scope ended
由于变量的可见性与其在代码中的位置直接相关,因此词法作用域对于能够对程序进行推理非常有帮助。这适用于:
- 程序员(既是为了他们自己对程序的推理,也是因为当事物具有词法作用域时可以检测和报告更多错误)
- 编译器(词法范围允许更容易和更好的优化)
- 诸如 IDE 之类的工具(对具有词法范围的事物进行分析和推理要容易得多)
在后来成为 Raku 的语言的早期设计过程中,子例程没有默认具有词法作用域(并且具有 our
作用域,就像在 Perl 中一样),但是人们意识到词法作用域是一个更好的默认值。进行子例程调用总是尝试解析具有词法作用域的符号,这意味着有可能在编译时报告未声明的子例程。此外,词法范围内的符号集在编译时是固定的,并且在声明性结构(如子例程)的情况下,例程以只读方式绑定到该符号。这也允许诸如多重分派的编译时解析、编译时参数检查等事情。 Raku 语言的未来版本很可能会指定越来越多的对词法作用域程序元素的编译时检查。
既然词法作用域这么好,为什么还要存在 our
(也称为包)作用域?简而言之,因为:
- 有时我们想要比在给定的词汇范围内更广泛地共享事物。我们可以只声明所有词法,然后标记我们想要与
is export
共享的东西,但是.. - 一旦我们到了使用大量不同库的地步,让所有东西都试图将事物导出到消费者的单一词法范围内可能会导致很多冲突
包允许符号命名空间。例如,如果我想在同一代码中为 HTTP 和 WebSockets 使用 Cro 客户端,我可以愉快地使用两者,并将它们分别称为 Cro::HTTP::Client
和 Cro::WebSocket::Client
。
包由 包声明符 引入,例如 class
、module
、grammar
和(有警告)role
. our
声明将在封闭的包结构中进行安装。
这些包最终存在于名为 GLOBAL
的顶级包中 - 这很合适,因为它们在全球范围内有效可见。如果我们声明一个 our
-scoped 变量,那么它就是一个全局变量(尽管希望是一个名称 spaced 的变量),关于它的文章已经写得足够多了,我们知道我们应该停下来思考一下,想知道是否全局变量是最好的 API 决定(因为最终,通过 GLOBAL
可见的所有内容都是 API 决定)。
然而,做 有点模糊的地方是我们可以词法包。这些是 GLOBAL
中未安装的软件包。我发现这些在进行 OO 编程时非常有用。例如,我可能有:
# This class that ends up in GLOBAL...
class Cro::HTTP::Client {
# Lexically scoped classes, which are marked `my` and thus hidden
# implementation details. This means I can refactor them however I
# want, and never have to worry about downstream fallout!
my class HTTP1Pipeline {
# Implementation...
}
my class HTTP2Pipeline {
# Implementation...
}
# Implementation...
}
词法包也可以嵌套并包含 our
范围内的变量,但最终不会全局可见(除非我们选择以某种方式将它们泄露出去)。
不同的 Raku 程序元素已被赋予默认范围:
- 子例程默认为词法 (
my
) 范围 - 方法默认为
has
范围(仅通过方法分派可见) - 类型(class、角色、语法、子集)和模块声明默认为包(
our
)范围 - 常量和枚举默认为包 (
our
) 作用域
实际上,最常共享的东西默认在包范围内,其余的则没有。 (变量确实迫使我们明确选择一个范围,但最常见的选择也是最短的输入范围。)
就我个人而言,我犹豫要不要让某个东西比语言默认设置更显眼,但是我通常会让它们不那么显眼(例如,my
用于内部使用的常量,以及用于构建实现细节的 classes)。当我可以通过在全局可见的包中公开一个 our
范围的变量来做某事时,我仍然经常喜欢让它成为 my
范围并提供一个 sub
(导出)或method
(由于在包范围 class
上可见)来控制对它的访问,以便在将来为自己购买一些灵活性。我认为现在做出错误的选择是可以的,如果我给自己 space 让他们在未来变得更正确而不给任何人带来不便。 :-)
总结:
- 对所有实施细节使用
my
范围 - 也对您计划
export
的事物使用my
范围,但请记住,导出会将符号放入消费者的单一词法范围内,并且存在名称冲突的风险,因此在导出特别通用的内容时要多加考虑名字 - 将
our
用于共享的内容,以及需要使用命名空间以避免冲突的时候 - 无论如何,我们最想共享的元素默认为
our
范围,因此显式编写our
应该让我们停下来思考一下