Skip to content

Class Data and Methods

Ovid edited this page Sep 14, 2021 · 7 revisions

Please see the main page of the repo for the actual RFC. As it states there:

Anything in the Wiki should be considered "rough drafts."

Click here to provide feedback.


In OO programming, you have the class and instances of that class. Most of the time we operate at the instance level, but there are times we need to operate at the class level before you have an instance.

common methods and variables

We propose the modifier/keyword common to indicate class methods or variables are shared across the class and all instances of this class.

common $foo;               # class data
common method foo () {...} # class method

Example

In Perl, the most common class method is new. However, there are other times we might need a class method, such as for a factory class, or for helper methods for building objects, but prior to the instantiation. For example, for a factory, we might have something like this:

class SomeFactory {
    has    $some_var;
    common $counter = 0;

    common method create($type) {
        my $target = $class->get_factory_type($type);
        $counter++;
        return $target->new;
    }

    common method get_factory_type($type) { ... }
}

In the above, instead of a $self variable being injected, we have a $class variable. It is not an instance. Instead, it's just an alias for the invoking package name. We cannot simply use __PACKAGE__ because if we subclass our factory, __PACKAGE__ in SomeFactory would be the parent package, not the child package.

In the create and get_factory_type methods, we have access to the $counter class data, but not to the $some_var instance data. that's because $some_var doesn't exist until an instance is created and class methods are called before that.

Roles

Note that common data and methods are attached to classes, not roles. Thus, if we have this:

role DoesSomething {
    common $count = 0;
    common method foo () {...}
}

class First  does DoesSomething {...}
class Second does DoesSomething {...}

The First and Second classes each have separate $count and foo methods. Thus, if you increment the $count value in the First class, the Second class's $count value will be unchanged.

Errors

class Foo {
    has    $instance_data;
    common $class_data;

    common method foo () {
        ...
    }

    method bar () {
        ...
    }
}

It should go without saying, but I've seen this trip up newer programmers in other languages enough that I'll say it anyway: instance variables and methods cannot be accessed by class methods. Thus, in the foo method, if we call $class->bar, we get an exception because that is an instance method and presumably its precise behavior (though not semantics, hopefully) can vary from instance to instance.

By the same token, foo cannot not access $instance_data because it can't know which value to pull from which instance.

However, instance methods can always call class methods or access class data because they are "global" for the class.

Subclassing

Generally speaking, a method is an instance or class method. An overriding method in a subclass should not be able to change this status.

Why not use subs?

For some this common behavior might seem like an over complication. Why not just do this?

sub create ($class, $type) {
    my $target = $class->get_factory_type($type);
    $counter++;
    return $target->new;
}

Sure, it's a touch more typing, but no new syntax!

The problem is that Corinna needs to understand if something is a method or a subroutine. If I call $some_class->foo, I want to know that I called a method and not some helper function that's been imported into this $some_class. By the same token, roles want to know that they're flattening methods into your class and not subroutines.

The lack of distinction between subroutines and methods has caused all sorts of curious workarounds to appear in Perl code. namespace::autoclean is a perfect example. Or just read through the code of various modules trying to implement roles to see the heuristics they go through to figure out what can and cannot be flattened into a consuming class.

Instance methods

As an aside, we can think of instance methods as being prefixed by has the same way instance variables are:

has $x;
has method foo () { ... }

However, we omit the has because this is the common case. Whether or not Corinna will support has method ... syntax is unknown at this time.