将问题简化为MRE:
class foo {}
say(foo.new); # Cannot stringify ...
简化解决方案:
class foo { method Str () { 'foo' } }
say(foo.new); # foo
总之,添加一个Str 方法。
这听起来很简单,但有很多幕后的东西需要考虑/解释。
nqp 与 raku
上述解决方案与 raku 使用的技术相同;当例程/操作期望值是字符串,但不是,语言行为是尝试强制转换为字符串。具体看是否有Str方法可以对值调用,如果有,调用。
在这种情况下,NQP's NQPMu 比raku's Mu 更准系统,不提供任何默认的Str 方法。所以一个解决方案是手动添加一个。
更一般地说,NQP 是一种非常有敌意的语言,除非您非常了解 raku 并且已经通过 A course on Rakudo and NQP internals。
一旦您掌握了该课程中的材料,我建议您将 IRC 频道 #raku-dev 和/或 #moarvm 视为您的第一个停靠港,而不是 SO(除非您的目标是专门增加 nqp/moarvm 的 SO 覆盖)。
调试编译器代码
如您所见,您链接的 NQP 代码在文件句柄上调用 .say。
然后调用this method。
该方法的主体是$str ~ "\n"。该代码将尝试将$str 强制转换为字符串(就像在 raku 中一样)。这就是生成“无法字符串化”错误的原因。
在 NQP 存储库中搜索“无法字符串化”仅匹配一些 Java 代码。而且我敢打赌你没有在 JVM 上运行 Rakudo。这意味着错误消息必须来自 MoarVM。
在 MoarVM 存储库中进行相同的搜索会产生 this line in coerce.c in MoarVM。
在包含该行的例程中向后看,我们看到this bit:
/* Check if there is a Str method. */
MVMROOT(tc, obj, {
strmeth = MVM_6model_find_method_cache_only(tc, obj,
tc->instance->str_consts.Str);
});
这显示了用 C 语言编写的后端,它正在寻找并调用一个名为 Str 的“方法”。 (它依赖于编译器的所有三层(raku、nqp 和后端)都遵循的内部 API (6model)。)
自定义Str 方法
您需要根据需要自定义Str 方法。例如,如果是类型对象,则打印类的名称,否则打印其$!bar 属性的值:
class foo {
has $!bar;
method Str () { self ?? nqp::coerce_is($!bar) !! self.HOW.name(self) }
}
say(foo.new(bar=>42)); # 42
尽管有方法名称,但 nqp say 例程不期望 raku Str 而是 nqp 本机字符串(最终成为 MoarVM 后端上的 MoarVM 本机字符串)。因此需要nqp::coerce_is(我通过浏览the nqp ops doc 发现)。
self.HOW.name(self) 是 nqp 没有 raku 所具有的细节的另一个例子。您可以在 raku 中编写相同的代码,但在 raku 中编写代码的惯用方式是 self.^name。