RFC 188, by Damian Conway: Objects: Private keys and methods

Break someone’s code today!

On September 1st of 2000 Damian Conway sent a proposal №188, promoting the idea of Private Keys and Methods.

In those days, Perl’s object-oriented programming relied heavily on hashes. Indeed, a hash can store data values by keys, as well as references to routines, which is how you can describe an object with data attributes and methods. With bits of syntax sugar here and there one can make shortcuts to work with such an object’s state and behavior.

So can be done in Raku nowadays:

my %tipsy-object;
# We have some data
%tipsy-object<created> = Date.new(now);
# And a "method"...
%tipsy-object<tell-age> = { say %tipsy-object<created> }
# Call it!
%tipsy-object<tell-age>(); # 2020-08-14
# Not method-y enough? Mkay, maybe this way...
%tipsy-object.<tell-age>(); # 2020-08-14
# Wow, what a dot it was... Now edit some data:
%tipsy-object<created> = Date.new(now).later(:1day);
%tipsy-object.<tell-age>(); # 2020-08-15, wow!

This sounds awesome in its simplicity (if we pretend Hash itself is not an object!) until you try to share your code. The moment you post this %tipsy-object people all around the World start to play with it even when you sleep and you have no idea what they might do. With best intentions, someone might write this:

my %tipsier-object;
die 'Woah this was not expected, calm down!!' unless %tipsy-object<created> ~~ Date;
%tipsier-object<tipsy-origins> = %tipsy-object<created>;

And now lying on a comfy sofa, Thinking about how someone might stare at your code even when you sleep, you notice a scary thing: you wanted to know when a %tipsy-object is created exactly, and so Date is just not nearly enough! Was it a morning, a bright day or a night? Who would tell the answer without mighty DateTime?

So you hurry up and do a little patch:

my %tipsy-object = created => DateTime.new(now), tell-age => { say %tipsy-object<created> }
%tipsy-object.<tell-age>();

What we just saw is a small, not so uncommon step for every programmer and a big leap for computer science: we broke someone’s code!

After all this rant from %tipsy-object users the next day, a lesson lived is a lesson learned: it is hard to ever underestimate the amount of assumptions people can do with your code, so spare them guessing, just tell the rules and hide everything else.

In object-oriented programming this very, very generic concept is spread across a couple of different ideas, one of which is named “encapsulation”.

In short, encapsulation allows us to say “Nobody should touch this… (unless someone really, really wants to make it happen)”. It gives you means to divide your coding efforts into a couple of different realms, one of which has “Nobody but I can use this” policy and its opposite takes the famous “Use it as you please and we will try really, really hard not to break anything knowingly” stance.

While encapsulation by itself is not a Great Code Problems Solver (you can write scary code anyways!) nor it reduces the amount of bugs (as if it happens!), but it certainly can save you some nerves during rewriting code (which sometimes is a Great Way To Solve Problems) and reduces coupling between components (which does reduce the amount of bugs, right? Hope never dies).

Here, inspired by the Tie::SecureHash module approach, Damian Conway has proposed a way to support encapsulation in a hash-based object-y system.

Sugar – the keyword way

The proposal describes a unary function named private, which can be applied to a single hash entry, a slice of entries or a whole hash:

private $hash{key};
# ^ can note the $ sigil to refer to a value
private @hash{qw(_name _rank _snum)};
# ^ can note the @ sigil to refer to a number of values
private %hash;
# ^ or, effectively, applied to any hash directly or via a reference

This function application restricts hash entries to the current package and makes all the spared ones “public”:

package MyClass;
sub new { bless private { secret => 'data' }, $_[0] }
package main;
my $obj = MyClass->new();
print $obj->{secret}; # dies, inaccessible entry

Such entries can be inherited:

package Base;
sub new {
my ($class, @data) = @_;
bless private { data => [@data] }, $class;
}
package SortableBase;
use base 'Base';
sub sorted {
my ($self) = @_;
print sort @{ $self->{Base::data} };
}

And, of course, for the aforementioned “someone really, really wants it to happen”, there was a Plan B prepared:

Although it is almost inevitably a Very Bad Idea and we shall probably All Come Regret To It Later, it ought to be possible to specify that certain entries of a private-ized hash are nevertheless public. This might, for example, be necessary when inheriting from legacy code.

The smartest hacks require the spookiest code anyway.

When it comes to methods, which are incidentally just subroutines with a twist or two, private becomes a prefix keyword:

package Base;
sub new { ... }
private sub check { ... }
sub do_check {
my ($self) = @_;
$self->check(); # okay
$self->Base::check(); # okay
check(); # okay
Base::check(); # okay
package Derived;
use base 'Base';
sub do_check {
my ($self) = @_;
$self->check(); # dies, no suitable accessible method
$self->Base::check(); # okay
Base::check($self); # okay
}
package main;
my $obj = Base->new();
$obj->check(); # dies, no suitable accessible method
$obj->Base::check(); # dies, no suitable accessible method
Base::check($obj); # dies, inaccessible subroutine

What about Raku?

Conclusion: Huffmanize it!

Raku wisdom says “Huffmanize your syntax”, as mighty Huffman coding it tells you to make things which are common to type easier to type.

With the Raku object system being ground up overhauled, the question of encapsulation was not forgotten! Not! In! A! Bit! (sorry for all the line noise you just heard).

Private things in Raku are marked with an exclamation mark. Firstly, it indicates to the reader those code bits are important. Secondly, for attributes it serves as a secondary “sigil”, namely “twigil”, which tells the reader that some interesting scoping is involved here. Thirdly, it looks like it has some class. Fourthly… Do you really expect more reasons here? Please, come up with your ideas in the comments!

Using Raku for some years, it suddenly strikes with a simple consistency here:

class Class {
# A public attribute
has $.unchecked = True;
# A private attribute
has $!checked = False;
# Just a Class package scoped sub
sub i'm-just-a-sub {}
# A public method
method check { True }
# A private method
method !check { False }
}

It is kind of amusing to see how exclamation marks mean the thing they mean in both cases, so if you once just remembered the syntax as given, now you can hopefully chuckle a bit.

Another common wisdom is “Similar things should look similar and different things should look different” and this approach, evolved from the proposal done, reflects this very nicely (just don’t tell anyone similar things also should look different, as that would be the next level of wisdom).

One thought on “RFC 188, by Damian Conway: Objects: Private keys and methods

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.