Privacy and Speed With Perl’s Object::Pad

Gear Shift

Last week found me exploring Object::Pad as an alternative to the Moo object-oriented framework for Perl since the former is prototyping the syntax and concepts for a proposed built-in OO framework named Corinna. I had to put that particular project on hold as dbcritic‘s current design is a bit too role-happy and Object::Pad currently lacks method modifiers as in Moo. (Corinna is explicitly skipping them for its current minimum viable product.) Thankfully, development continues at a rapid pace. For instance, author Paul Evans has already addressed a problem I ran into when attempting to examine slot values in the debugger.

But I wanted to highlight a point I made in one of the comments last week: Object::Pad’s slots (a.k.a. fields, attributes, whatever) are private by default, completely unexposed to other class instances unless they monkey with the meta-object protocol. Unless you explicitly define or generate some kind of accessor method, these slots act like lexical (a.k.a. my) variables and are only available to methods within the class.

Here’s an example:

say “But only I can see $private_slot.”;

package main {
my $obj = Local::MyClass->new(arg => ‘foo’);
say $obj->readable_slot;

# Nope: Not a HASH reference
try { say $obj->{private_slot} } catch ($e) { say “Nope: $e” }

# Nope: Can’t locate object method “private_slot” via package “Local::MyClass”
try { say $obj->private_slot } catch ($e) { say “Nope: $e” }
” data-lang=”text/x-perl”>

use v5.14; # for say and package blocks
use Object::Pad 0.50;
use Feature::Compat::Try;

class Local::MyClass {
    has $arg           :param  = 'hello';
    has $readable_slot :reader="world";
    has $private_slot="shh";

    method show_slots {
        say "You passed me $arg in the constructor.";
        say "I can see $readable_slot and you can use it as a reader.";
        say "Here's me using the reader too: ", $self->readable_slot;
        say "But only I can see $private_slot.";

package main {
    my $obj = Local::MyClass->new(arg => 'foo');
    say $obj->readable_slot;

    # Nope: Not a HASH reference
    try { say $obj->{private_slot} } catch ($e) { say "Nope: $e" }

    # Nope: Can't locate object method "private_slot" via package "Local::MyClass"
    try { say $obj->private_slot } catch ($e) { say "Nope: $e" }

This stands in stark contrast to Perl’s more low-tech hashref-based objects, where all attributes are available simply through dereferencing the instance, e.g., $object->{foo}. Although discouraged, OO purists sometimes ding Perl for this kind of unenforced encapsulation, and I myself have seen codebases that violate it despite the convention of preceding private method and attribute names with an underscore (_).

Unfortunately, there is not yet any way to declare an Object::Pad method private. You could use lexical subroutines, but then you lose the convenience of a pre-made $self variable and accessibility through the MOP. The Corinna proposal lists several different types of methods including private ones, so maybe this is an area for future Object::Pad development.

Another open question from the comments: “How is [Object::Pad] on memory and speed compared to Moo and blessed objects?” Luckily the prolific perlancar has already added Object::Pad to his Bencher::Scenarios::Accessors distribution, and from that, it appears that between it and Moo, Object::Pad is faster on startup, neck-and-neck on object construction and accessor generation, and slower on reads and writes. (Note that Object::Pad is a fast-moving target so these figures may not track with the latest version’s changes.) It’s no surprise that plain blessed objects fared better than both in most scenarios except for reads, where Moo was faster than hash-based objects but slower than array-based.

I expect that should Corinna be built into Perl it would narrow that gap with blessed objects, but in my mind, the advantages of using an object system outweigh the performance hit 95% of the time. As far as benchmarking memory goes, I still need to test that on a Linux box (maybe my new VPS?) once I get more familiar with the Bencher framework.

Source link

Leave a Reply

Shopping cart


No products in the cart.

Continue Shopping