Perl 6 的多重分派如何决定使用哪个例程?

How does Perl 6's multi dispatch decide which routine to use?

考虑这个程序,我在参数列表中构造了一个数组。虽然有一个接受数组的签名,但这调用了接受列表的签名:

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 )版本呢?调度员如何做出决定(记录在何处)?

Why doesn't it call the ( Array @array ) version?

你的测试 foo 调用只有一个数组 ([1,2,3]) 作为参数,而不是 of Arrays 的数组(例如[[1,2,3],[4,5,6]]).

(@array 中的 @ 表示 does Positional 的值,例如数组或列表。Array @array 表示相同的东西,但具有附加约束数组、列表或其他任何元素的每个元素都是 Array.)

How is the dispatcher making its decision?

简化,它正在选择最窄的匹配类型:

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

(Diagram of subtype relationships of Array, List and Positional.)

有关更多详细信息,请参阅

(and where is it documented)?

我不确定文档。 Multi-dispatch 看起来非常简约。

设计文档(更完整但更过时)和文档(已知不完整,如文档所述。perl6.org 承认,但希望更及时).前者在Synopsis 12中解释了多子分辨率。摘录:

When you call a routine with a particular short name, if there are multiple visible long names, they are all considered candidates. They are sorted into an order according to how close the run-time types of the arguments match up with the declared types of the parameters of each candidate. The best candidate is called, unless there's a tie, in which case the tied candidates are redispatched using any additional tiebreaker strategies (see below). [...]

There are three tiebreaking modes, in increasing order of desperation:

A) inner or derived scope

B) run-time constraint processing

C) use of a candidate marked with "is default"

Tiebreaker A simply prefers candidates in an inner or more derived scope over candidates in an outer or less derived scope. For candidates in the same scope, we proceed to tiebreaker B.

In the absence of any constraints, ties in tiebreaker A immediately failover to tiebreaker C; if not resolved by C, they warn at compile time about an ambiguous dispatch. [...]

我对 Perl 6 的了解还不够,无法证明其准确性,但它似乎与 一致,并且还涵盖了其他方面。

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

由于它在做一些不同的事情,即使数据结构匹配也不会成功:

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 缺点之一。