Introducing KBOS

Starting even before Moose, we (in the Perl 5 world) have a plethora of Modules extending the syntax of the language with Perl 6 and more in mind. The following article sums up not only my 2 and a half cents on the subject but also an attempt to implement it. It should be of interest to anybody thinking about programming in general.

As many here know, Kephra is the project closest to my heart and during the latest iteration, I decided to extend the language itself to get a more expressive, less repetitive code base. I want a fast, extendable type system with helpful error messages, real private attributes, real private methods, signatures with typed, positional, named and optional arguments, relaxed professional error handling, I want to know all instances of a class, reuse by delegation and incorporate any foreign objects. Last not least should the system support me in marshalling all attributes, so I can fully restore a program state after restart or switch into a remote session / other window.

The Kephra Base Object System (KBOS - read: ok boss) is designed to deliver on all that and I just want to discuss here my decisions. Some seem to be strange, like no inheritance (a feature), attribute types (not even Raku has them) or 4 different method scopes. But hej its my pile of garbage, stay away. I want this to become the optimal object system for Kephra's needs. It is not clear to me if I will release it or parts as a separate distribution in future.

For today maybe I present the rationale behind the 4 tier scope system, because that is what I did yesterday at the HalleLeipzig.pm meeting. Future posts will be dealing with other aspects.

Privacy Please

Given a class (starting the the class keyword) named Class and a bunch of methods and attributes (with their specific keywords) in a loaded file that got compiled. To create an object, just call $obj = Class->new( $arg, ....);. So far, so expectable. say ref $obj, outputs of course Class, but inside a method my $self = shift; say ref $self; will give you Class::PRIVATE - a very different name space, where all the public and private methods are present. These private methods are not present in Class. How did I achieve this ? - by Keyword::Simple. If the KBOS-enhanced Perl-sources contain

private method (...sig def..) { .... code ...}

the keyword method triggers during BEGIN (compile)-time a Keyword::Simple - callback. That rewrites the source code (slightly tamed source filter) into

Kephra::Base::Class::Builder::create_method('private', "...sig def..", sub { ... code });

This altered source will be compiled by perl and run. Because only Perl can parse Perl I let perl deal with the code inside the sub and the resulting coderef will be mounted into the right name space by calling ...Builder::create_method. Of course there is happening much more, but that is the important part for todays topic.

Attribute ACCESS

Attributes are not accessible at all, because what is the point of having attribute types if you can not enforce them and let every schmock put any value into them. To avoid that, I auto generate getters and setters that check for type constrains. But even these Methods live in a scope so obscure, you have to know the internals to access them (unless you define them as private or public). The reason for this hassle is two-fold.

One - you want to get a well regulated access to each attribute separately. Some attributes can be seen by everybody, but only written inside the object. But some data you want to keep in the background just noting a state you can not influence directly by a command, not even in private methods. That is why inside an accessor methods (getters and setters) the $self has a different scope, than in private or public ones. In that access scope (Class::ACCESS) you can see all public and private methods, plus the getter and setter that live only in access scope.

The second reason for that model has to do with the lack of inheritance. Instead of inherit methods from an different class you use an object of that class as an attribute. So you get a code structure, where your private methods are the working logic of your class, that should be as straightforward and dwimmy as possible. Your public methods are the translations and API to the outward world and accessors are the same to the inner world (attribute data and attribute objects). Naturally you want to encapsulate that inner layer from the outer too. Then changing an attribute type affects only code of the inner layer.

To not forget the main thing: each accessor, which is not auto generated, is assigned to one attribute and gets the full access to this one attribute (but can not override the type checks ofc, which are done my the auto generated, inner setter).

BUILD scope

The fourth scope is again much simple to explain. Whey your inside the constructor (new) or deconstructor (demolish) you want to have full access. I mean at some point the value of an attribute has to be set. So in BUILD scope you see all the previously mentioned get/ set methods of all attributes. This way you can have attributes that can only be set at start and only be even seen when the object is demolished.

Next time I will talk more about the type system - its the real base of it all.

6 Comments

I'm interested in how well this technique for method privacy works out.

For me, the golden test would be something like (pseudo code):

class Base {
  private method foo () {
    say "ok";
  }
  public method bar () {
    $self->foo();
  }
}
class Derived {
  extends Base;
  public method foo () {
    say "not ok";
  }
}

If Derived has a public method called foo is where things get more tricky. Like, should Base::bar see its private method Base::foo, or should it see Derived::foo?

If Base will see Derived::foo, I think Derived should at least get a compile-time warning about overriding a private method with a public method.

Generally speaking, I find using lexicals for private methods to be a good solution.

class Base {
  my $foo = method () {
    say "ok";
  };
  public method bar () {
    $self->$foo();
  }
}
class Derived {
  extends Base;
  public method foo () {
    say "not ok";
  }
}

The call syntax Base::bar uses to call Base::foo is kinda ugly, but it works, and it's obvious what's happening.

Sorry, first example should have been:

class Base {
  private method foo () {
    say "ok";
  }
  public method bar () {
    $self->foo();
  }
}
class Derived {
  extends Base;
  private method foo () {
    say "not ok";
  }
}

Hmm, but what if Base were written using KBOS, and Derived was just a plain old Perl package with use parent "Base";? Even if you're not implementing facilities for inheritance, it's hard to ignore that inheritance does exist in Perl.

Leave a comment

About lichtkind

user-pic Kephra, Articles, Books, Perl, Programming