When we left off, we had a Node implementation in Cor/Object::Pad, called CorNode. Sticking with that convention, and following the Moose Cookbook, I jumped ahead to Points! Included is a cheet sheet to remind me what the has attributes do.

use Object::Pad;

use strict;
use warnings;
use feature qw{ say postderef signatures };
no warnings qw{ experimental };

package CorPoint 0.01;

# because :param,   $x->new(x=>1)
# because :reader,  $x->y
# because :mutator, $x->q = 1
# because :mutator, $y=$x->q
# because :writer,  $x->set_r('Poe')

class CorPoint {
    has $x :param :reader = 0;
    has $y :param :reader = 0;
    has $q :mutator = 0;
    has $r :writer = 'Ovid';

    method xy () {
        return join ', ',$x,$y;
    }

    method rrr () { return $r }

    BUILD {
        say 'BUILD: '. $self->xy;
    }
}
#!/usr/bin/env perl

use strict;
use warnings;
use feature qw{ say signatures state };
no warnings qw{ experimental };

use lib '/mnt/c/Users/jacob';
use CorPoint;

my $i = CorPoint->new( x => 1, y => 1 );
my $j = CorPoint->new( x => 2, y => 3 );
my $k = CorPoint->new();
my $l = CorPoint->new( q => 7 );

display_hack( $i, $j, $k, $l );
$l->set_r('Poe');
display_hack( $i, $j, $k, $l );

sub display_hack( @arr ) {
    say '-' x 20;
    say join "\t", qw{ reference x y xy q writer };
    for my $p (@arr) {
        say join "\t", ref $p, $p->x, $p->y, $p->xy, $p->q, $p->rrr,;
    }
}
$ ./points.pl
BUILD: 1, 1
BUILD: 2, 3
BUILD: 0, 0
BUILD: 0, 0
--------------------
reference       x       y       xy      q       writer
CorPoint        1       1       1, 1    0       Ovid
CorPoint        2       3       2, 3    0       Ovid
CorPoint        0       0       0, 0    0       Ovid
CorPoint        0       0       0, 0    0       Ovid
--------------------
reference       x       y       xy      q       writer
CorPoint        1       1       1, 1    0       Ovid
CorPoint        2       3       2, 3    0       Ovid
CorPoint        0       0       0, 0    0       Ovid
CorPoint        0       0       0, 0    0       Poe

I’m not sure if I’ll do anything with BUILD. If there was a distance_to_origin method, I could compute it in BUILD and keep it as a variable, but that would mean I couldn’t change my x and ys without explicitly doing it, or just make it a method. But it’s hear so we can see that it works and where it shows up. (I’ve not made ADJUST work, either. Not being sure what I could do if it worked, I’m leaving it be.)

But the Moose Cookbook jumps to extending Point into Point3D by adding a z. I mean, clearly, there’s no reason for a functional, non-research Point object to have q and r, right? So I’m kinda leaning that way>?

Anyway…

use Object::Pad;

use strict;
use warnings;
use feature qw{ say postderef signatures };
no warnings qw{ experimental };

use lib '.';
use CorPoint;

package CorPoint3D 0.01;

class CorPoint3D isa CorPoint {
    has $z :param :reader = 0;

    method xyz () {
        return join ', ', $self->x, $self->y, $self->z;
    }

    method xy :override () {
        return join ',', $self->x, $self->y;
    }
}

In context:

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw{ say signatures state };
no warnings qw{ experimental };

use Cwd qw( abs_path );
use lib '/mnt/c/Users/jacob';
use CorPoint3D;

my $i = CorPoint3D->new( x => 1, y => 1 );
my $j = CorPoint3D->new( x => 2, y => 3, z => 4 );
my $k = CorPoint3D->new();
my $l = CorPoint3D->new( q => 7 );

display_hack( $i, $j, $k, $l );
$l->set_r('Poe');
display_hack( $i, $j, $k, $l );

sub display_hack( @arr ) {
    say '-' x 20;
    say join "\t", qw{ reference x y xy xyz q writer };
    for my $p (@arr) {
        say join "\t", ref $p, $p->x, $p->y, $p->xy, $p->xyz, $p->q, $p->rrr,;
    }
}
BUILD: 1,1
BUILD: 2,3
BUILD: 0,0
BUILD: 0,0
--------------------
reference       x       y       xy      xyz     q       writer
CorPoint3D      1       1       1,1     1, 1, 0 0       Ovid
CorPoint3D      2       3       2,3     2, 3, 4 0       Ovid
CorPoint3D      0       0       0,0     0, 0, 0 0       Ovid
CorPoint3D      0       0       0,0     0, 0, 0 0       Ovid
--------------------
reference       x       y       xy      xyz     q       writer
CorPoint3D      1       1       1,1     1, 1, 0 0       Ovid
CorPoint3D      2       3       2,3     2, 3, 4 0       Ovid
CorPoint3D      0       0       0,0     0, 0, 0 0       Ovid
CorPoint3D      0       0       0,0     0, 0, 0 0       Poe

Here we add z as both a param (so new->CorPoint3d(z=>3) works, as does $obj->z), as well as an xyz method, that, like CorPoint’s xy method, returns a comma-separated string of the coordinates.

Because I was playing, I also overrid that xy method to remove the space in the concatenation. What I’m thinking should come soon is before, after and around, which I’ve seen used in anger. I think that, if you have the proper modern infrastructure, you don’t need to have before and around as much to adjust the objects into the form you need. But I’m just an unfrozen caveman programmer, and these new ideas confuse and frighten me.

So, now that I have subclassing, I think the next thing is to use these in typing. I’m thinking that I should make a line that takes two points and has a length method. Or, a CorLine that takes two CorPoints, and I use another in a more Roles context to force the length method, which then needs to redo the line into CorLine3D using CorPoint3Ds instead of CorPoints. I guess I’m seeing the sense behind types that I’ve missed so far.

I like these experiments so far. I’m beginning to see the use for them, kinda. But a common go-to for me is filling data into a hashref and feeding that to Template Toolkit. I know what happens in TT when I give it a scalar, but what happens when I give it a CorPoint? It seems to add problems without solving them, at least in my most common use cases. Then again, I think I write fairly trailing-edge Perl.

If you have any questions or comments, I would be glad to hear it. Ask me on Twitter or make an issue on my blog repo.