“P6opaque, Str” 与 Perl 6 中的简单“Str”类型

“P6opaque, Str” vs simple “Str” types in Perl 6

这是 的后续。

我终于能够在此处重现错误:

my @recentList = prompt("Get recentList: e.g. 1 2 3: ").words || (2,4,6);    
say "the list is: ", @recentList;    
for @recentList -> $x {
    say "one element is:  ", $x;
    say "element type is: ", $x.WHAT;
    say "test (1,2,3).tail(\"2\") : ", (1,2,3).tail("2");
    say ( (10.rand.Int xx 10) xx 15 ).map: { @($_.tail($x)); };
}

只要我使用默认列表,只需在提示符下点击 return 并且不输入任何内容,结果就可以了。但是如果我输入一个数字,它会给出这个错误:

Get recentList: e.g. 1 2 3: 2
the list is: [2]
one element is:  2
element type is: (Str)
test (1,2,3).tail("2") : (2 3)
This type cannot unbox to a native integer: P6opaque, Str
  in block  at intType.p6 line 9
  in block <unit> at intType.p6 line 5

如果 tail("2") 有效,为什么 tail($x) 会失败?此外,在我的原始代码中, tail($x.Int) 不会纠正问题,但它在这里做到了。

这充其量只是一个 nanswer。这是一个 thus-far 试图解决这个问题的失败尝试。我可能只是在杂草丛中徘徊。但我会发布我所拥有的。如果不出意外,也许它可以作为一个提醒,下面的前三个步骤是明智的;此后,我赌自己有能力通过探索源代码来继续前进,而我可能会按照第三步中讨论的那样直接调试编译器来取得更快、更可靠的进展。


好的,第一步是 MRE。您提供的是一个 E,它完全是 R 并且足够 M。:)


第 2 步是增加 M(打高尔夫球)。我把它归结为:

Any.tail('0');    # OK
Any.tail('1');    # BOOM

注意可以是实际值:

1.tail('1');      # BOOM
(1..2).tail('1'); # BOOM

但有些值有效:

(1,2).tail('1');  # OK

第 3 步可能 应该 遵循 Playing with the code of Rakudo Perl 6 中的说明来跟踪编译器的执行,例如在其源代码中添加 says并重新编译它。

您可能还想试试 App::MoarVM::Debug。 (我没有。)

使用这些方法,您将能够绝对精确地跟踪编译器对您输入的任何代码执行的操作。我建议你这样做,即使我没有这样做。也许你能找出我哪里出错了。


在下文中,我通过直接探索 Rakudo 编译器的源代码来跟踪这个问题。

一个search for "method tail" in the Rakudo sources yielded 4 matches. For my golf the matching method is a match in core/AnyIterableMethods.pm6.

tail 参数 $n 显然不是 Callable 所以继续我们的探索的相关行是 Rakudo::Iterator.LastNValues(self.iterator,$n,'tail').

对此进行搜索会在 core/Iterator.pm6 中找到 this method

这又调用了this .new routine

These three lines:

nqp::if(
  n <= 0,                 # must be HLL comparison
  Rakudo::Iterator.Empty, # negative is just nothing

解释为什么 '0' 有效。 <= 运算符在进行数字比较之前将其操作数强制转换为数字。所以 '0' 强制转换为 0,条件是 True,结果是 Rakudo::Iterator.EmptyAny.tail('0') 产生 () 并且不报错.

紧跟在上面三行之后的代码是nqp::ifelse分支。它以 nqp::create(self)!SET-SELF(iterator,n,f).

结束

这又会调用 !SET-SELF 例程,其中包含以下行:

($!lastn := nqp::setelems(nqp::list, $!size = size)),

尝试将 size(在我们的 BOOM 案例中是 '1')分配给 $!size。但是 $!size is declared as:

has int $!size;

宾果。


或者是吗?我不知道我是否真的 正确地找到了问题所在。我只是在探索 github 存储库中的代码,实际上并没有 运行 编译器的检测版本并跟踪其执行,正如在试图找出问题的明智步骤 #3 中所讨论的那样你遇到过。

更糟糕的是,当我运行编译器时,它是一个旧编译器,而我正在探索的代码是 master...


为什么这样做?

(*,*).tail('1') # OK

此代码路径大概是 this method。参数 $n 不是 Callable,因此代码路径将 运行 通过在以下行中使用 $n 的路径:

              nqp::unless(
                nqp::istype($n,Whatever) || $n == Inf,
                $iterator.skip-at-least(nqp::elems($!reified) - $n.Int)

$n == Inf 应该不是问题。 == 会将其操作数强制转换为数字,并且应该注意 $n'1'.

nqp::elems($!reified) - $n.Int应该也不是问题。

nqp ops 文档显示 nqp::elems always returns an int。所以这归结为一个应该工作的int - Int

嗯。

A blame of these lines shows that the .Int in the last line was only added 3 months ago.

所以,抓住救命稻草,如果尝试会发生什么:

(my int $foo = 1) - '1' # OK

不,这不是问题。

这条路似乎变冷了,或者我已经偏离了实际的执行路径。

我会发布我得到的东西。也许其他人可以从这里拿起它,或者我会在一三天内再去一次...