How to print an object in NQP ? (For debugging purposes)
It is easy in Raku:
class Toto { has $.member = 42; }
class Titi { has $.member = 41; has $.toto = Toto.new }
my $ti = Titi.new;
say $ti;
# Titi.new(member => 41, toto => Toto.new(member => 42))
dd $ti;
# Titi $ti = Titi.new(member => 41, toto => Toto.new(member => 42))
- It seems more complicated in NQP
class Toto { has $!member; sub create() {$!member := 42}};
class Titi { has $!member; has $!toto; sub create() {$!member := 41; $!toto := Toto.new; $!toto.create; }}
my $ti := Titi.new;
say($ti);
Cannot stringify this object of type P6opaque (Titi)
Of course, no .gist
method, the code calls nqp::encode
which finally expects a string.
Reducing the problem to an MRE:
Simplifying the solution:
In summary, add a
Str
method.This sounds simple but there's a whole lot of behind-the-scenes stuff to consider/explain.
nqp vs raku
The above solution is the same technique raku uses; when a value is expected by a routine/operation to be a string, but isn't, the language behavior is to attempt to coerce to a string. Specifically, see if there's a
Str
method that can be called on the value, and if so, call it.In this case NQP's
NQPMu
, which is way more barebones than raku'sMu
, doesn't provide any defaultStr
method. So a solution is to manually add one.More generally, NQP is a pretty hostile language unless you know raku fairly well and have gone thru A course on Rakudo and NQP internals.
And once you're up to speed on the material in that course, I recommend you consider the IRC channels #raku-dev and/or #moarvm as your first port of call rather than SO (unless your goal is specifically to increase SO coverage of nqp/moarvm).
Debugging the compiler code
As you will have seen, the NQP code you linked calls
.say
on a filehandle.That then calls this method.
That method's body is
$str ~ "\n"
. That code will attempt to coerce$str
to a string (just as it would in raku). That's what'll be generating the "Cannot stringify" error.A search for "Cannot stringify" in the NQP repo only matched some Java code. And I bet you're not running Rakudo on the JVM. That means the error message must be coming from MoarVM.
The same search in the MoarVM repo yields this line in
coerce.c
in MoarVM.Looking backwards in the routine containing that line we see this bit:
This shows the backend, written in C, looking for and invoking a "method" called
Str
. (It's relying on an internal API (6model) that all three layers of the compiler (raku, nqp, and backends) adhere to.)Customizing the
Str
methodYou'll need to customize the
Str
method as appropriate. For example, to print the class's name if it's a type object, and the value of its$!bar
attribute otherwise:Despite the method name, the nqp
say
routine is not expecting a rakuStr
but rather an nqp native string (which ends up being a MoarVM native string on the MoarVM backend). Hence the need fornqp::coerce_is
(which I found by browsing the nqp ops doc).self.HOW.name(self)
is another example of the way nqp just doesn't have the niceties that raku has. You could write the same code in raku but the idiomatic way to write it in raku isself.^name
.