调试函数调用

Debugging into function calls

我想知道,在这段代码中,

sub might-sink {
    return [1,2,Failure.new("boo!"),"still here"];
}

might-sink;
say might-sink;

sink context actually calls List.sink, that is, does nothing. My hunch is that it does, but I'm not sure if that's the case or it's simply not reified中第一次调用might-sink,所以直到后来才抛出失败。 我已经尝试了 debugging options here,但是其中 none 进入了从这个调用的代码。所以两个问题,分开但相关:

  1. 如何使用 Perl 6 调试器或模块调试这些函数?
  2. 如何确定 List.sink 是否实际被调用,或者通常情况下,如果对象在接收器上下文中发现自身时 sink 是否被调用?

即使您明确调用 .sink 那个 return 值,Failure 也不会爆炸:

sub might-sink {
    return [1,2,Failure.new("boo!"),"still here"];
}

might-sink.sink;
say "Did not explode";
# OUTPUT: Did not explode

代码中的爆炸发生在 say 调用期间 .gist 的再现期间,该调用遍历 Array 的前 100 个元素并对其调用 .gist。当它在 Failure 元素上这样做时,它会引爆它。

您将下沉列表对象本身和下沉它包含的单个值混为一谈。您正确地提到 List.sink 是空操作,但是 Seq.sink 会消耗自身¹,但它也不会爆炸,因为只有 Seq 正在沉没,而不是它的各个元素:(+"a", 42).Seq.sink

如果你想强制潜在的 Failure 爆炸,你可以:

1: use fatal。这将导致编译器致命失败,导致它们在 Failures 传递的许多地方爆炸。这是一个词法编译指示,它也会在 try {} 个块中自动启用。

use fatal;
sub might-sink {
    return [1,2,Failure.new("boo!"),"still here"];
}
might-sink;

# OUTPUT:
# boo!
#  in block <unit> at

2:.self 方法调用。它由 Mu 提供(因此被所有对象继承)并且只是 returns self,所以它是一个空操作(除了执行 decont)。但是,由于大多数调用 armed Failure 的方法都会导致它们引爆,因此调用 .self 会导致它们爆炸。

sub might-sink {
    return [1,2,Failure.new("boo!"),"still here"];
}
might-sink».self;

# OUTPUT:
# boo!
#  in block <unit> at

How can I debug into those functions using the Perl 6 debugger or a module?

我自己不使用调试器,但是 perl6-debug-m 应该预装在您的 Rakudo 安装中。它需要您安装 Debugger::UI::CommandLine module. And you just use that instead of perl6 to run your program and it should offer some instructions on the command line on what it does. There's also a blog post on it and a video(需要 Flash 才能播放)。

还有一个最近发布的 alpha 质量 App::MoarVM::Debug 可以让您远程调试程序。它的美妙之处在于它可以让您转储对象内脏并在程序运行时导航它们 运行,所以如果您能忍受它的界面,它会很方便。

就我个人而言,我调试时只使用 dd 例程将内容转储到战略点,看看它是否包含我希望它包含的内容。

How can I find out if that List.sink is actually called or, in general, if sink is called when an object finds itself in sink context?

您可以使用 doesbut 运算符混入一个角色,这些运算符提供一个 sink 方法来执行您可以观看的操作。喜欢打印一些东西:

sub foo {
    return [<a b c>] does role { 
        method sink { say "sink called" }
    }
}
foo
# OUTPUT: sink called

更高级的方法是转储 post-优化 QAST tree and seeing if it contains the calls you expect it to. CoreHackers::Q 模块使转储的 QAST 树更易于可视化。

P.S.: 关于隐含下沉的话题,值得注意的是 R#1571 列举了几个错误并建议完全重新设计处理下沉的内部系统。


¹ - 更准确地说,Seq.sink 在其 Iterator 上调用 .sink-all,默认情况下它只是简单地使用值,但自定义迭代器可以重写该方法是一个 no- op也是。