How does Perl 6's multi dispatch decide which

2019-04-09 22:00发布

Consider this program where I construct an Array in the argument list. Although there's a signature that accepts an Array, this calls the one that accepts a List:

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" }

I get the output from an unexpected routine:

Called Array $ version

If I uncomment that other signature, that one is called:

Called List version

Why doesn't it call the ( Array @array ) version? How is the dispatcher making its decision (and where is it documented)?

3条回答
爷的心禁止访问
2楼-- · 2019-04-09 22:42

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

Your test foo call has just an Array ([1,2,3]) as its argument, not an Array of Arrays (eg [[1,2,3],[4,5,6]]).

(The @ in @array indicates a value that does Positional, eg an array or a list. Array @array indicates the same thing but with the additional constraint that each element of the array, list or whatever is an Array.)

How is the dispatcher making its decision?

Simplifying, it's picking the narrowest matching type:

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.)

For lots of details see jnthn's authoritative answer to a related SO question.

(and where is it documented)?

I'm not sure about the doc. Multi-dispatch looks pretty minimal.

查看更多
疯言疯语
3楼-- · 2019-04-09 22:45

There seems to be a trade off between the design documents (more complete but more outdated) and the documentation (known to be incomplete, as docs.perl6.org acknowledges, but hopefully more up-to-date). The former explains multisub resolution in Synopsis 12. Excerpt:

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. [...]

I don’t know enough about Perl 6 to testify as to its accuracy, but it seems to be in agreement with raith’s answer, and covers additional ground as well.

查看更多
【Aperson】
4楼-- · 2019-04-09 22:52

I made a really dumb mistake, and that's why I wasn't seeing what I expected. You can't constrain a variable that starts with @. Any constraint applies to its elements. The Array @array denotes that I have a positional sort of thing in which each element is an Array. This is the same thing that raiph said. The odd thing is that the grammar looks the same but it does different things. It's something I've tripped over before.

Since it's doing something different, it's not going to work out even if the data structure matches:

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" }

I still get the version I wouldn't expect based on the constraint and the data structure:

Called Array $ version
Called Array $ version

I think this is just going to be one of Perl 6's warts that normal users will have to learn.

查看更多
登录 后发表回答