Adjective Noun

Everyone Loves Porgs

2018-02-17 08:35, Tags: perl roles

It's been a while. I have several post ideas in various stages of completion, and it's hard to prioritise that over life sometimes... So I figure I need to start posting shorter ideas and things I've been playing with, lest this turn into one of those blogs that never updates. So here we go.

Classes are really easy to define in Perl 6. They're so easy that I find myself using them to encapsulate small Hash-like things, where I also want maybe one or two methods

class Contact {
    has $.name;
    has $.phone;
    has $.bday;
    method age {
        (Date.new($.bday), *.later(:1year) ...^ * > Date.today).end
    }
}

Yes, that's an inefficient way to calculate age... Like a lot of things in life, that method gets slower the older you are.

Anyways, now I have defined a simple little class for holding some data together, but to actually instantiate one I have to bust out some named arguments.

my @contacts;
@contacts.push: Contact.new(:name<John>, :phone<555-1111>, :bday<1940-10-09>);

Who's got time for all those characters? Sometimes I just want to build them with positional args, but that means writing a custom multi method new to handle those cases... but I'm just throwing together a quick & dirty class, is it really worth my time to build a custom constructor?

So I started playing around, and created a Role which lets me build my class with Positional arguments... or an Array.. or List... and hey, I threw in a Hash for free!

@contacts.push: Contact.new('James', '555-1112', '1942-06-18');

@contacts.push: Contact.new(< George 555-1113 1943-02-25 >)

my %hash = name => 'Richard', phone => '555-1114', bday => '1940-07-07';
@contacts.push: Contact.new(%hash);

I used the introspection method .^attributes to get a list of attributes. I'm only interested in local attributes (not inherited ones), though you certainly could change that, or even control it via a Parametrized Role. I'm also only interested in attributes that have an accessor (ie. public attributes).

role Porgs {
    multi method new(*@args where *.elems) {
        self.bless: |%(
            self.^attributes(:local)
                .grep(*.has_accessor)
                .map(*.name.substr: 2)
            Z=> @args)
    }
    multi method new(List $args) {
        self.new: |$args
    }
    multi method new(%args) {
        self.bless: |%args
    }
}

class Contact does Porgs { ... }

I called the role Porgs, which is a contraction of "Positional Args", but also shares the name of a creature from Star Wars. The Porgs role allows you to write classes which are small and cute, much like the creature. Also, everyone loves Porgs.

So that's all for today. I 'm not planning on publishing this to the ecosystem or anything, so feel free to steel this idea, improve upon it, rename it and publish it yourself to the ecosystem if you so desire. Also, I'm not sure if the fact that self.^attributes returns the attributes in the order you declare them is an is an implementation detail... so perhaps that might change?