在Perl 6中,“P6opaque,Str”和简单的“Str”类型有什么区别?

7

这是我之前问题的追踪.

我现在终于可以在此处重现错误:

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)); };
}

只要我在提示处按回车使用默认列表,结果就可以。但是如果我输入一个数字,就会出现以下错误:
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)不能解决问题,但在这里却可以。

很酷。 :) 第一步是编写表现问题的代码。第二步是将其压缩。到目前为止,我有 (1 xx 1).tail('1')。 :) - raiph
好的,回到这个问题。say Any.tail('0') 显示 (),但是 say Any.tail('1') 产生了 This type cannot unbox to a native integer: P6opaque, Str。我想这已经足够了。接下来,一些代码挖掘... - raiph
1个回答

6

这只是一个至多的答案,是迄今为止尝试解决这个问题失败的尝试。我可能已经走进了泥淖。但我会公布我所拥有的东西。如果说没有其他作用,也许它可以作为一个提醒:以下的前三步是明智的;此后,我在赌我的能力,通过探索源代码来推进,当我可能通过直接调试编译器来讨论第三步而取得更快更可靠的进展。


好的,第一步是进行MRE。你提供的是一个完全R且足够M的E。 :)


第二步是增加M(高尔夫)值。我将其降至:

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

请注意,它可以是实际值:

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

但是一些值是有效的:

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

第三步应该是按照Playing with the code of Rakudo Perl 6的说明来跟踪编译器的执行,例如在其源代码中添加say并重新编译。

您还可以尝试使用App::MoarVM::Debug。(我没有尝试过。)

使用这些方法,您将拥有绝对精确地跟踪编译器处理任何代码的能力。我建议您这样做,即使我没有这样做。也许您可以找出我的错误所在。


以下我将通过直接深入挖掘Rakudo编译器的源代码来跟踪这个问题。
在Rakudo源代码中搜索"method tail",找到了4个匹配项。对于我的高尔夫比赛,匹配方法是"core/AnyIterableMethods.pm6"中的一个匹配项
参数$n显然不是一个Callable,因此继续我们的探究的相关行是Rakudo::Iterator.LastNValues(self.iterator,$n,'tail')

搜索结果指向 core/Iterator.pm6 中的 该方法

该方法又调用了 .new例程

以下三行代码

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赋值给$!size,在我们的BOOM案例中,size的值为'1'。但是$!size被声明为

has int $!size;

或者不是这样吗?我不确定我是否已经正确地追踪到了问题所在。我只是在github仓库中浏览代码,而没有实际运行编译器的受控版本并跟踪其执行,正如我们讨论的尝试解决你遇到的问题的明智步骤#3。

更糟糕的是,当我运行编译器时,它是旧版的,而我正在浏览的代码是master...


为什么这个有效?
(*,*).tail('1') # OK

这将要使用的代码路径可能是此方法。参数$n不是Callable,因此代码路径将通过在以下行中使用$n的路径运行:
              nqp::unless(
                nqp::istype($n,Whatever) || $n == Inf,
                $iterator.skip-at-least(nqp::elems($!reified) - $n.Int)
< p > $n == 无穷大 不应该是一个问题。 == 将强制其操作数为数字,这应该可以处理 $n '1' 的情况。

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

nqp ops 文档显示 nqp::elems 总是返回一个 int 。 因此,这归结为 int - Int,应该可以工作。

嗯。

这些代码行的错误表明最后一行中的.Int仅于3个月前添加

因此,试图抓住救命稻草,如果尝试:

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

不,那不是问题所在。

看起来线索已经断了,或者说我已经偏离了实际的执行路径。

我会发布我所拥有的。也许其他人可以从这里接手,或者我会在一两天后再试一次...


1
非常感谢你,raiph!!!你总是在解释方面非常详细。我看到答案比我想象的更深入,我将尝试深入了解App::MoarVM::Debug和Rakudo源代码。这本身就是一次艰巨的冒险。再次感谢你,raiph!!! - lisprogtor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接