bri*_*foy 11 perl6 method-resolution-order multimethod raku
考虑这个例子,其中子类有一个没有签名的multi方法和一个带有slurpy参数的方法:
class Foo {
multi method do-it { put "Default" }
multi method do-it ( Int $n ) { put "Int method" }
multi method do-it ( Str $s ) { put "Str method" }
multi method do-it ( Rat $r ) { put "Rat method" }
}
class Bar is Foo {
multi method do-it { put "Bar method" }
multi method do-it (*@a) { put "Bar slurpy method" }
}
Foo.new.do-it: 1;
Foo.new.do-it: 'Perl 6';
Foo.new.do-it: <1/137>;
Foo.new.do-it;
put '-' x 10;
Bar.new.do-it: 1;
Bar.new.do-it: 'Perl 6';
Bar.new.do-it: <1/137>;
Bar.new.do-it: 5+3i;
Bar.new.do-it;
Run Code Online (Sandbox Code Playgroud)
方法查找是如何构建的?我正在寻找一种方法来解释它,特别是没有抱怨它.
Int method
Str method
Rat method
Default
----------
Int method
Str method
Rat method
Bar slurpy method
Bar method
Run Code Online (Sandbox Code Playgroud)
有一个呼叫Bar的do-it用1的实例.一些合理的人可能会认为它首先寻找匹配的签名,Bar而且这种签名不会让任何事情超越它.然而,调用在继承链中找到了正确的多元.
是否Bar已经知道所有签名?是否搜索或者所有这些东西在编写时已经解决了?
并且,有没有办法在运行时找出哪个类提供了方法?也许有人打电话给HOW?这将是一个方便的调试工具,当我有一个多我错误指定并正在其他地方处理.
Jon*_*ton 12
多次调度时要记住的关键是它发生在子或方法解析之后.因此,所有多次发送实际上都是一个两步过程.这两个步骤也是相互独立的.
写这样的东西时:
multi sub foo($x) { }
multi sub foo($x, $y) { }
Run Code Online (Sandbox Code Playgroud)
编译器将生成一个:
proto sub foo(|) {*}
Run Code Online (Sandbox Code Playgroud)
也就是说,除非你proto自己写了一个子.这proto是实际安装到lexpad中的内容; 一个multisub永远不会直接安装到lexpad中,而是安装到候选列表中proto.
因此,在调用multisub时,该过程是:
protoproto,选择最佳multi候选人并拨打电话当multi嵌套作用域中有候选项时,proto将从外部作用域克隆并将其安装到内部作用域中,并将候选项添加到克隆中.
使用多种方法时会发生非常类似的过程,除了:
}类,角色或语法proto可以由角色或类提供,因此与multi候选者组成角色也会将它们添加到待办事项列表中proto,但是父类有这样的proto,那将被克隆; 否则proto将成为空意味着对多方法的调用是:
protoproto,选择最佳multi候选人并拨打电话完全相同的排序和选择算法用于多子方法和多方法.就多分派算法而言,调用只是第一个参数.此外,Perl 6多重调度算法不会比后面的参数更严重地加权先前的参数,因此就像:
class A { }
class B is A { }
multi sub f(A, B) { }
multi sub f(B, A) { }
Run Code Online (Sandbox Code Playgroud)
将被视为绑定,并在调用时给出模糊的调度错误f(B, B),因此将定义:
class B { ... }
class A {
multi method m(B) { }
}
class B is A {
multi method m(A) { }
}
Run Code Online (Sandbox Code Playgroud)
然后调用B.m(B),因为多dipsatcher再次看到类型元组(A, B)和(B, A).
多次派遣本身关注的是狭隘的概念.如果C1的至少一个参数是比C2中相同位置的参数更窄的类型,则候选C1比C2窄,并且所有其他参数都是并列的(即,不是更窄,不是更宽).如果反之则为真则则更宽.否则,它是并列的.一些例子:
(Int) is narrower than (Any)
(Int) is tied with (Num)
(Int) is tied with (Int)
(Int, Int) is narrower than (Any, Any)
(Any, Int) is narrower than (Any, Any)
(Int, Any) is narrower than (Any, Any)
(Int, Int) is narrower than (Int, Any)
(Int, Int) is narrower than (Any, Int)
(Int, Any) is tied with (Any, Int)
(Int, Int) is tied with (Int, Int)
Run Code Online (Sandbox Code Playgroud)
多dipsatcher构建候选的有向图,其中当C1比C2窄时,从C1到C2存在边缘.然后它会找到没有传入边缘的所有候选项,并将其删除.这是第一批候选人.移除将产生一组没有传入边缘的候选者,然后将其移除并成为第二组候选者.这样下去,直到所有的候选人都从图中获得,或者,如果我们到达那里,我们可以从图中没有的状态(一种非常罕见的情况,但是这将被报告给程序员为圆).这个过程发生一次,而不是每次发送,它产生一组候选人.(是的,它只是一种拓扑排序,但分组细节对于接下来的内容非常重要.)
当呼叫发生时,搜索组以便匹配候选者.如果同一组中的两个候选者匹配,并且没有绑定断路器(命名参数,where条款或隐含where条款,subset类型,解包或或is default),则将报告模糊的发送.如果搜索了所有组而未找到结果,则调度将失败.
关于arity(必需参数beats可选参数或slurpy)和is rw(它比没有其他方面相同的候选者更窄)也存在一些狭隘的考虑因素is rw.
一旦发现一组中的一个或多个候选人匹配,则考虑打破连接器.这些包括命名参数,where子句和解包的存在,并在第一场比赛胜利的基础上工作.
multi f($i where $i < 3) { } # C1
multi f($i where $i > 1) { } # C2
f(2) # C1 and C2 tied; C1 wins by textual ordering due to where
Run Code Online (Sandbox Code Playgroud)
请注意,此文本排序仅适用于打破平局; 就类型而言,源代码中候选者的顺序并不重要.(这个命名参数也只是作为打破平局有时令人惊讶.)
最后,我会注意到,虽然多次调度的结果总是与我所描述的两步过程相匹配,但实际上会发生大量的运行时优化.虽然所有查找最初都按照描述完全解析,但结果会放入调度缓存中,这比查找拓扑排序所提供的组的查找速度快得多.这样安装可以完全绕过proto的调用,从而节省了一个callframe.如果您可以看到此行为的工件--profile; proto与多候选者相比,任何基于类型的调度(没有断路器)的自动生成将接收少量呼叫.如果您在自己编写自定义逻辑,这不适用proto.
除此之外,如果您在MoarVM上运行,动态优化器可以更进一步.它可以使用收集和推断的类型信息来解析方法/子调度和多调度,将两步过程转换为0步过程.小型候选人也可以内联到调用者中(同样,分析器可以告诉您内联已经发生),这可以说是将多调度转换为-1步骤过程.:-)
默认情况下,Rakudo Perl 6方法查找过程由Metamodel :: MROBasedMethodDispatch角色完成.有关相应的源代码,请参阅Rakudo的/src/Perl6/Metamodel/MROBasedMethodDispatch.nqp.
(默认情况下,它使用角色Metamodel :: C3MRO,它实现了C3方法解析顺序.请参阅Rakudo的/src/Perl6/Metamodel/C3MRO.nqp获取源代码.)
.^find_method返回基于短名称(不带参数)的匹配方法.每当短名称对应多种方法时,此返回的方法就是原型.
调用.candidatesproto会返回与proto匹配的Method对象列表.(调用.candidates非proto方法只返回与单元素列表中唯一元素相同的方法.)
for Bar.^find_method('do-it').candidates -> $method {
$method.signature.say;
}
Run Code Online (Sandbox Code Playgroud)
这使:
(Foo $: *%_)
(Foo $: Int $n, *%_)
(Foo $: Str $s, *%_)
(Foo $: Rat $r, *%_)
(Bar $: *%_)
(Bar $: *@a, *%_)
Run Code Online (Sandbox Code Playgroud)
该Bar.new.do-it: 5+3i;调用传递一个Bar为self加上5+3i位置参数.来自候选列表的最接近这些参数的签名(又名"最窄匹配")就是那个(Bar $: *@a, *%_).因此调用具有该签名的例程.
该Bar.new.do-it;呼叫传递的是Baras,self而不是其他内容.该(Bar $: *%_)签名是更近的(更窄的)匹配比(Bar $: *@a, *%_).同样,调用具有最接近(最窄)签名的例程.
| 归档时间: |
|
| 查看次数: |
377 次 |
| 最近记录: |