2017-06-19 59 views
4

考虑这个程序,我在参数列表中构造一个数组。虽然有这么接受一个数组的签名,这个调用接受列表中的一个:Perl 6的多派遣如何决定使用哪个例程?

foo([ 1, 2, 3 ]); 

multi foo (Array @array) { put "Called Array @ version" } 
multi foo (Array $array) { put "Called Array \$ version" } 
multi foo (List $list) { put "Called List version" } 
multi foo (Range $range) { put "Called Range version" } 

我得到的输出从一个意想不到的例行:

Called Array $ version 

如果我取消,其他签名,那一个被称为:

Called List version 

为什么它不叫(Array @array)版本?调度员如何做出决定(以及它在哪里记录)?

+1

cf'say( - > Array @ {})。signature.params [0]。输入' – Christoph

+0

@briandfoy我已经删除了关于逆转(减少噪音)的评论。随意删除您的确认评论。稍后我会删除这一个。 :) – raiph

+0

briandfoy您还没有接受答案。我愿意加倍努力。我想我完全解决了“为什么不叫...?”。我想我完全解决了“调度员如何做出决定?”因为它与你的具体例子有关。我认为@斯莱德的回答更广泛地解决了这个问题。但你写道:“这并不能解释我所表现出的行为。”根据我对他们的回答的评论,我认为它的确如此。我可以扩大我的答案,“它在哪里记录?”通过链接到对应斯莱德的答案的文档。我很感谢你的指导。 :) – raiph

回答

1

我犯了一个非常愚蠢的错误,这就是为什么我没有看到我的预期。您无法约束以@开头的变量。任何约束都适用于其元素。 Array @array表示我有一个位置排序的东西,其中每个元素是Array。这是the same thing that raiph said。奇怪的是,语法看起来一样,但它做了不同的事情。这是我以前绊倒过的东西。

,因为它是在做不同的东西,这不是去上班了,即使数据结构相匹配:

foo([ [1], [2], [3] ]); 
foo([ 1, 2, 3 ]); 

multi foo (Array @array) { put "Called Array @ version" } 
multi foo (Array $array) { put "Called Array \$ version" } 
multi foo (List $list) { put "Called List version" } 
multi foo (Range $range) { put "Called Range version" } 

我仍然得到的版本我不希望基于约束和数据结构:

Called Array $ version 
Called Array $ version 

我认为这只是普通用户必须学习的Perl 6的一个瑕疵。

5

为什么不调用(Array @array)版本?

您的测试foo调用只有一个阵列([1,2,3])作为它的参数,而不是一个数组的Array小号(如[[1,2,3],[4,5,6]])。

(在@array@指示值does Positional,例如数组或列表。Array @array表示同样的事情,但与所述附加约束或阵列,列表的每个元素无论是Array。)

调度员如何做出决定?

简化,它的采摘最窄的匹配类型:(Diagram of subtype relationships of Array, List and Positional

multi foo (Array  )    {} # Narrowest 
multi foo (List  )    {} # Broader 
multi foo (Positional )    {} # Broader still 
multi foo (@array  )    {} # Same as `Positional` 

对于很多细节见jnthn's authoritative answer to a related SO question

(以及它在哪里记录)?

我不确定该文档。 Multi-dispatch看起来很小。

-1

似乎在设计文档(更完整但更过时)和文档(已知不完整,docs.perl6.org承认,但希望更新)之间存在权衡。前者解释了Synopsis 12中的多分辨率分辨率。摘录:

当您调用具有特定短名称的例程时,如果有多个可见的长名称,则它们都被视为候选者。根据参数的运行时间类型与每个候选参数的声明类型的匹配程度,将它们排序为一个顺序。最好的候选人被称为,除非有平局,在这种情况下,并列候选人使用任何额外的平局策略(见下文)重新分配。 [...]

有三种打破平局模式,在绝望的顺序增加:

A)内或衍生范围

B)的运行时约束处理

C)使用的候选的标记“是默认的”

Tiebreaker A只是比其他派生范围内的候选人更偏爱内部派生范围或更多派生范围内的候选人。对于同一范围内的候选人,我们继续执行决胜局B.

在没有任何约束的情况下,tiebreaker A中的联系人立即故障转移到tiebreaker C;如果没有被C解决,他们会在编译时警告一个模糊的调度。 [...]

我不太了解Perl 6来证明它的准确性,但它似乎与raith’s answer一致,并且还涵盖了其他方面。

+0

这并不能解释我显示的行为。 –

+1

@briandfoy Imo第一部分涵盖了你所表现出来的行为:“当你用一个特定的短名称调用一个例程时,如果有多个可见的长名字,它们都被认为是候选者,它们按照多近的顺序排序参数的运行时类型与每个候选者的参数的声明类型相匹配,最好的候选者被调用,除非有平局......“没有平局,所以匹配类型最接近的候选人会赢。 – raiph

2

这是关于“容器”。我不能提出一个完整的规则,但能够展示一些例子。

有了明确兴建货柜my @array

my Array @array = [ [1], [2], [3] ]; 

foo(@array); 

multi foo (Array @array) { put "Called Array @ version" } 
multi foo (Array $array) { put "Called Array \$ version" } 

# Ambiguous call to 'foo'; these signatures all match: 
# :(Array @array) 
# :(Array $array) 

如果我们做出一些改变:

my @array = [ 1, 2, 3 ]; 

foo(@array); 

multi foo (@array) { put "Called Array @ version" } 
multi foo (Array $array) { put "Called Array \$ version" } 

# Called Array $ version 

看来Array $array太窄。 my @array = [1, 2, 3]@arrayPositional

foo([ 1, 2, 3 ]); 

multi foo (@array) { put "Called Array @ version" } 
multi foo ($array) { put "Called Array \$ version" } 

# Called Array @ version 

foo([ 1, 2, 3 ]); 

multi foo ($array) { put "Called Array \$ version" } 

# Called Array $ version 

回到第一实例,Array @array意味着该参数是与类型约束阵列的位置的事情,而Array $array意味着它是一个阵列(在某种程度上更“窄”比位置),结果仍然是平局。