Re: H9w do I print a structure?

[email protected] (Tirifto) Sat, 12 Apr 2025 17:14:22 +0000
Newsgroups perl.perl6.users
Message-ID <[email protected]>
Hello!

Both `print` and `say` convert their argument to a string, but they do 
so by calling different methods on the argument. `print` calls the 
method `.Str`, while `say` calls the method `.gist`. On custom classes 
(like your PartitionClass), calling `.Str` will only return the object’s 
identifier by default, but calling `.gist` (or `.raku`) will return code 
you can run to re-create the object. So to get the same output with 
`print` like you did with `say`, only without the newline, you can do 
`print $Partition.gist` or `print $Partition.raku`. (`.gist` might not 
show the entire code if the text gets too long, so you might prefer to 
use `.raku`.)

Calling `.kv` doesn’t work, because objects (/unlike/ maps / tables) 
aren’t transparent structures that you can see the insides of. From the 
perspective of OOP, objects are opaque, and the only proper way to look 
inside is by calling their methods. So if you want to be able to see all 
of the object’s attributes, you should *give the class a method* that 
will return all of the object’s attributes in some form. You could 
override the `.Str` or `.gist` methods if you simply want to print the 
attributes in a nice form, or you could override the `.kv` method if you 
might want to do something else with them. Here’s an example:

    [0] > class PartitionClass{
        has Str $.DeviceID          is rw;
        has Str $.VolumeName        is rw;
        has Str $.ProviderName      is rw;
        has Str $.UNC_BackupPath    is rw;
        has Int $.DriveType         is rw;
        has Str $.DriveTypeStr      is rw;
        has Int $.FreeSpace         is rw;
        has Int $.Size              is rw;
        has Int $.PercentUsed       is rw;
        has Int $.PercentRemaining  is rw;

        #| Return a list of pair objects: (Attribute name => Attribute
    value)
        method pairs(--> Iterable) {
            :$.DeviceID, :$.VolumeName, :$.ProviderName,
            :$.UNC_BackupPath, :$.DriveType, :$.DriveTypeStr,
            :$.FreeSpace, :$.Size, :$.PercentUsed, :$.PercentRemaining;
        }

        #| Return a sequence of attribute names and values interleaved.
        method kv(--> Iterable) {
            $.pairs.map({ .kv }).flat;
        }

        #| Return a string presenting all attributes on a single line.
        method Str(--> Str) {
            $.pairs.map({ .key ~ “: ” ~ .value }).join(“, ”) ~ “.”;
        }
    }
    (PartitionClass)

    [1] > my $Partition = PartitionClass.new(
        DeviceID         => "",
        VolumeName       => "",
        ProviderName     => "",
        UNC_BackupPath   => "",
        DriveType        => 0,
        DriveTypeStr     => "Unknown",
        FreeSpace        => 0,
        Size             => 0,
        PercentUsed      => 0,
        PercentRemaining => 100
    );
    PartitionClass.new(DeviceID => "", VolumeName => "", ProviderName =>
    "", UNC_BackupPath => "", DriveType => 0, DriveTypeStr => "Unknown",
    FreeSpace => 0, Size =>
    0, PercentUsed => 0, PercentRemaining => 100)

    [2] > for $Partition.kv -> $i, $j { print "i <$i>   j <$j>\n" }
    i <DeviceID>   j <>
    i <VolumeName>   j <>
    i <ProviderName>   j <>
    i <UNC_BackupPath>   j <>
    i <DriveType>   j <0>
    i <DriveTypeStr>   j <Unknown>
    i <FreeSpace>   j <0>
    i <Size>   j <0>
    i <PercentUsed>   j <0>
    i <PercentRemaining>   j <100>

    [2] > print $Partition ~ "\n";
    DeviceID: , VolumeName: , ProviderName: , UNC_BackupPath: ,
    DriveType: 0, DriveTypeStr: Unknown, FreeSpace: 0, Size: 0,
    PercentUsed: 0, PercentRemaining: 100.

    [2] > say $Partition;
    PartitionClass.new(DeviceID => "", VolumeName => "", ProviderName =>
    "", UNC_BackupPath => "", DriveType => 0, DriveTypeStr => "Unknown",
    FreeSpace => 0, Size =>
    0, PercentUsed => 0, PercentRemaining => 100)

Of course, both the code and the formatting of the output could be 
improved. :-)

You could also ask Raku to list all of the object’s attributes and then 
find each of their values for your object. This pretty much violates the 
principles of OOP, so it might not be something you want your programs 
to rely on, but if you just need to inspect an object’s internal state 
for your personal use, it’s probably fine! An example:

    [2] > for $Partition.^attributes { say “{.name}: {.get_value:
    $Partition}” }
    $!DeviceID:
    $!VolumeName:
    $!ProviderName:
    $!UNC_BackupPath:
    $!DriveType: 0
    $!DriveTypeStr: Unknown
    $!FreeSpace: 0
    $!Size: 0
    $!PercentUsed: 0
    $!PercentRemaining: 100

If there are better ways, I’m afraid I don’t know of them. Hope this 
helps, though! ^ ^
// Tirifto

On 11. 04. 25 8:19, ToddAndMargo via perl6-users wrote:
> Hi All,
>
> I am not having any luck printing the values of a OOP
> structure, except for printing them one at a time.  I
> can get it to messily print with `say`, but I want to
> do it with `print` so I can control the line feeds,
> comments, etc..
>
> What am I doing wrong?
>
> Many thanks,
> -T
>
>
> [0] > class PartitionClass{
>     has Str $.DeviceID          is rw;
>     has Str $.VolumeName        is rw;
>     has Str $.ProviderName      is rw;
>     has Str $.UNC_BackupPath    is rw;
>     has Int $.DriveType         is rw;
>     has Str $.DriveTypeStr      is rw;
>     has Int $.FreeSpace         is rw;
>     has Int $.Size              is rw;
>     has Int $.PercentUsed       is rw;
>     has Int $.PercentRemaining  is rw;
> }
> (PartitionClass)
>
>
> [1] > my $Partition = PartitionClass.new(
>     DeviceID         => "",
>     VolumeName       => "",
>     ProviderName     => "",
>     UNC_BackupPath   => "",
>     DriveType        => 0,
>     DriveTypeStr     => "Unknown",
>     FreeSpace        => 0,
>     Size             => 0,
>     PercentUsed      => 0,
>     PercentRemaining => 100
> );
> PartitionClass.new(DeviceID => "", VolumeName => "", ProviderName => 
> "", UNC_BackupPath => "", DriveType => 0, DriveTypeStr => "Unknown", 
> FreeSpace => 0, Size => 0, PercentUsed => 0, PercentRemaining => 100)
>
>
> [2] > for $Partition.kv -> $i, $j { print "i <$i>   j <$j>\n" }
> i <0>   j <PartitionClass<4875316684000>>
>
>
> [2] > print $Partition ~ "\n";
> PartitionClass<4875316684000>
>
>
> [2] > say $Partition;
> PartitionClass.new(DeviceID => "", VolumeName => "", ProviderName => 
> "", UNC_BackupPath => "", DriveType => 0, DriveTypeStr => "Unknown", 
> FreeSpace => 0, Size => 0, PercentUsed => 0, PercentRemaining => 100)
>