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

时间:2019-05-06 19:55:57

标签: perl6 raku

使子程序可重用是模块的一项核心功能,我认为这是一种语言的基本组成方式,它可以在程序员的工作时间之内构成并有效:

如果您在模块中创建类型,则可以创建自己的模块,该模块添加一个对您的类型进行操作的子类。我不必扩展您的模块即可。

const getSelectedUser = (userId) => users.find(({id}) => id === userId);

Overloading operators for a class中可以看到,模块的这种可组合性对操作员同样有效,这对我来说很有意义,因为无论如何,操作员还是某种潜艇的语法糖(至少在我的脑海中如此)。请注意,此类运算符的定义不会导致现有代码的行为发生任何令人惊讶的变化,您需要将其显式导入到代码中才能访问它,就像上面的子代码一样。

鉴于此,我发现我们没有类似的方法机制,这很奇怪,例如How do you add a method to an existing class in Perl 6?的讨论,尤其是因为perl6是一种方法友好的语言。如果要扩展现有类型的用法,则需要使用与编写原始模块相同的样式来实现。如果Int上有一个.is-prime,那么我必须可以添加一个也是半素对吧?

我在上面的链接中阅读了讨论,但是不太赞同“远距离操作”的论点:与我从模块中导出另一个多子对象有什么不同?例如,对我而言,将其变成词法更改(特质+ impl ...的)的生锈方法对我来说似乎很卫生,并且与上面的运算符方法非常一致。

(至少对我而言)比技术上更有趣的是语言设计是否存在以下问题:语言能力不是为现有名词(类型)提供新动词(子代,运算符,方法)的一种核心设计目标像perl6一样?如果是,为什么会以不同的方式对待方法?如果确实有充分的理由将它们区别对待,这是否意味着我们正在使用许多非可组合方法作为名词,而应该使用subs?

1 个答案:

答案 0 :(得分:17)

从语言设计的角度来看,这全都归结为一个简单的问题:我们在说哪种语言?在Perl 6中,这是一个我们总是要非常清楚的问题。

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

相比之下,方法调用是要用目标对象的语言解释的动词。这是Perl 6的动态后期绑定部分。尽管最直接的结果是在OO实现中以各种形式发现的典型多态性,这要归功于元编程,甚至动词的解释方式也得以实现。抢。例如,monitor在解释动词并随后释放它时将获得一个锁。其他对象可能是constructed based on things other than Perl 6 code,因此动词的解释并不意味着调用以Perl 6方法编写的代码。或者代码可能在网络上的某个地方。谁知道?好吧,当然不是呼叫者,这就是后期绑定的意义,力量和风险。

Perl 6的答案是“我想用我的当前语言扩展此对象可以使用的动词范围”非常简单:使用与扩展当前语言有关的语言功能!甚至还有一种特殊的语法$obj.&foo,它允许以动词foo在当前语言中定义-通过编写sub-然后像在方法上那样调用它宾语。但是,语法上的微小区别使读者-以及编译器-清楚正在发生什么,以及使用哪种语言定义该动词。

通过使用augment可以扩展某些对象定义的语言。但是,考虑到它会产生全局影响并且还会分散对象语言的定义,因此它几乎不是最好的处理方法。

我们在编程中所做的很多事情都是关于构建语言的。我的意思不是说新语法。我们的大多数新语言-即使是像Perl 6一样易于变异的语言-都是使用标准语言功能定义的名词和动词。但是,在任何不平凡的程序中,我们无法一次记住每种语言的每个细节。当我去餐厅订购炸肉排时,我不知道如何将订单传送到厨房,厨房的样子,炸肉排是否按需锤打,裹面包屑和煮熟,或者只是从(希望不是很陈旧)准备好的炸肉排的缓存。我和厨房的共同含义足以使正确的事情发生,但是我不知道他们将如何对我的要求做出准确的反应,他们也不需要知道我该如何做。这种想法被OO本身认可-至少在我们完全拥抱它的同时-并且在更大范围内被域驱动设计中发现的诸如边界上下文之类的概念认可。

总而言之,Perl 6试图帮助我们保持语言通俗易懂:仅通过有限的理解就可以了解当前语言中的内容以及所表达的内容。该区别由sub / method区别编码,这也证明是悬挂静态/动态区别的明智之地。