当前位置: 首页 > 知识库问答 >
问题:

为什么将方法添加到类型与在perl6中添加sub或运算符不同?

权浩邈
2023-03-14

使子程序/过程可重用是模块的核心功能之一,我认为这是一种语言如何在程序员的时间内可组合并因此高效的基本方式:

如果您在模块中创建一个类型,我可以创建自己的模块,添加一个对您的类型进行操作的子模块。我不需要扩展你的模块来做到这一点。

# your module
class Foo {
    has $.id;
    has $.name;
}

# my module
sub foo-str(Foo:D $f) is export {
    return "[{$f.id}-{$f.name}]"
}

# someone else using yours and mine together for profit
my $f = Foo.new(:id(1234), :name("brclge"));
say foo-str($f);

正如在重载一个类的操作符中所看到的那样,模块的可组合性同样适用于操作符,这对我来说是有意义的,因为操作符只是sub的某种语法糖(至少在我看来)。请注意,这样一个操作符的定义不会导致现有代码的行为发生任何令人惊讶的变化,您需要显式地将其导入到您的代码中才能访问它,就像上面的子部分一样。

有鉴于此,我觉得很奇怪,我们没有类似的方法机制,例如,请参阅关于如何在Perl 6中向现有类添加方法的讨论?,尤其是因为perl6是一种快乐的语言。如果我想扩展现有类型的使用,我希望以与原始模块相同的样式来完成。如果有。是Int上的素数,我必须可以添加一个。也是半素数,对吗?

我阅读了上面链接中的讨论,但不太相信“远距离操作”的论点:这与我从模块中导出另一个multi-sub有什么不同?例如,使这成为一个词汇变化(Trait impl…for)的方法对我来说似乎很卫生,并且与上面的操作符方法非常一致。

比技术细节更有趣的(至少对我来说)是语言设计的问题:为现有名词(类型)提供新动词(子句、运算符、方法)的能力不是像perl6这样的语言的核心设计目标吗?如果是,为什么它会区别对待方法?如果它确实因为一个好的原因而区别对待它们,这难道不意味着我们使用了许多不可组合的方法作为名词,而我们应该使用subs吗?

共有1个答案

尚宏硕
2023-03-14

从语言html" target="_blank">设计的角度来看,这一切归结为一个简单的问题:我们在说哪种语言?在Perl 6中,这是一个我们一直试图弄清楚的问题。

Perl 6中当前语言的概念完全是根据词法范围定义的。子声明是词汇范围的。当我们从模块导入符号时,包括额外的multi候选符号,这些符号是词汇范围的。当我们进行语言调整时——比如引入新的操作符——这些都是词汇范围。我们当前语言中的动词——即子程序调用——是那些具有词汇定义的动词。(操作符只是具有更有趣解析的sub调用。)由于词法作用域在编译时结束时关闭,因此编译器拥有当前语言的完整视图。这就是为什么在编译时检测并报告对不存在的subs的子调用,或对未声明变量的引用,以及一些基本的编译时类型检查;未来的Perl 6版本可能会扩展预期的编译时检查集。当前语言是Perl6的静态、早期绑定的一部分。

相比之下,方法调用是要用目标对象的语言解释的动词。这是Perl6的动态、后期绑定部分。虽然最直接的结果是在OO实现中以各种形式发现的典型多态性,但由于元编程,甚至动词的解释方式也可以被采用。例如,监视器将在解释动词时获取锁,然后释放它。其他对象可能是基于Perl 6代码以外的内容构建的,因此动词的解释并不意味着调用以Perl 6方法编写的代码。或者代码可能在网络上的某个地方。谁知道呢?当然不是来电者,这就是延迟绑定的重点、力量和风险。

Perl 6对“我想扩展当前语言中可用于此对象的动词范围”的回答非常简单:使用与扩展当前语言相关的语言功能!甚至还有一种特殊的语法,$obj。

通过使用augment可以扩展某些类型对象定义的语言。然而,这很少是最好的方法,因为它会产生全局效应,并且会分散对象语言的定义。

我们在编程中做的很多事情都是关于构建语言的。我说的不是新语法;我们的大多数新语言——即使是在像Perl 6这样容易变异的语言中——只是使用标准语言功能定义的名词和动词。然而,在任何非琐碎的程序中,我们不可能一次记住每种语言的每一个细节。当我去餐馆点炸肉排时,我不知道订单是如何被运送到厨房的,厨房是什么样子的,炸肉排是根据需要锤出来、涂上面包屑和烹饪的,还是只是从一个(希望不会太不新鲜))准备好的炸肉排的缓存。厨房和我有足够的共同意义来做正确的事情,但是我不知道他们会如何准确地回应我的要求,他们也不需要知道我在此期间会做什么。这种想法被面向对象本身所认可——至少当我们完全接受它的时候——并且在更大的范围内被诸如领域驱动设计中发现的有界上下文等概念所认可。

总之,Perl 6试图帮助我们保持我们的语言直截了当:知道我们当前语言中的内容,以及我们在有限的理解下表达的内容。这种区别是由sub/方法区别编码的,这也是挂静态/动态区别的一个明智的地方。

 类似资料:
  • 错误: 代码: 自<代码>列表

  • 问题内容: 我希望能够在javascript中说出这样的话: 如何将自己的距离函数添加到字符串类? 问题答案: 您可以扩展原型; …并像这样使用它;

  • 问题内容: 据我了解,a 是定义为 一些特定但未知类型 的 列表 。因此,无论此参数的类型是什么,都应该对其进行扩展,因为在Java中,不能有任何不扩展的类型。那么为什么下面的代码没有被编译呢?它如何违反了不变式 问题答案: 可以指向可以存储不同类型对象的任何类型的列表。 您如何看待,例如,让它添加任何对象是否安全?

  • 请帮助我了解什么是好的设计在这里,详细。

  • 问题内容: 因此,我想在字符串中添加一个字符,在某些情况下,我想将该字符加倍,然后将其添加到字符串中(即先添加到字符串本身)。我尝试如下所示。 这引发了一个错误,但是我已经在字符串中添加了一个字符,所以我尝试了: 哪个有效。为什么在求和中包含字符串会导致它起作用?是否添加了一个字符串属性,该字符串属性由于存在字符串而只能在字符转换为字符串时才能使用? 问题答案: 这是因为String + Char

  • 问题内容: 我继承了其他开发人员编写的一些JavaScript代码。他不喜欢我们在整个项目中使用的网格组件,因此他决定编写自己的网格组件。他写的表格无法对日期进行排序,因为它只能绑定到字符串/数字。他在使用日期之前将所有日期转换为字符串。我查看了他编写的date函数的字符串格式,发现可以将具有原始值的date属性添加到字符串中,然后在排序时查看该字符串是否具有date属性并基于此进行排序。但是,似