NAME

perlclass - Modern cperl classes and roles

SYNOPSIS

To declare classes and roles:

    class NAME           { }  # declare a class
    role NAME            { }  # declare a role
    class NAME is PARENT { }  # inherit from one or more classes
    class NAME does ROLE { }  # or roles
    role NAME is PARENT  { }
    role NAME does ROLE  { }

    class NAME {
      has $a;                 # declare a field
      method new ($a?) {      # not needed, inherited from Mu
        bless [$a], ref $self ? ref $self : $self;
      }
    }
    class NAME :native {      # native C-struct with 2 native int fields
      has int $a = 0;         # for the ffi
      has int $b = 0;
    }

    class NAME :open {        # allows run-time monkey-patching, adding new methods.
      has $a = 0;             # disables most compile-time optimizations.
      has $b = 0;
    }

    my CLASS $obj = CLASS->new; # default object constructor
    $obj->field;                # the getter method
    $obj->field = 1;            # the setter method

DESCRIPTION

This document is a reference to Modern Perl programming in cperl with the introduction of five new builtin keywords: class role method multi has, enabling the features and performance of perl6 object orientation into perl5.

A class or a role is a special package, declared as a block with subroutines, methods and has fields, with dynamic run-time inheritance from parent classes, compile-time composition of roles, but unlike perl6 a class or role is closed by default. I.e. the class namespace and the @ISA and @DOES arrays are readonly. Old open dynamic classes can easily be declared with the old package syntax, but methods and the other new syntax is forbidden there. Only multi subroutines are allowed outside of classes.

Classes define types, and a hierarchy of types. All variables can be optionally typed referring to a class, which can be a builtin class like the coretypes or a user-defined class. See perltypes. This is the same as in perl6, but fundamentally different to all the naive ad-hoc perl5 OO extensions, which treat types as unnecessary and slow extensions. Types do make cperl faster and safer.

The root of the cperl type hierarchy is Mu, every class is a Mu. The MOP is defined in the Metamodel hierarchy, exactly as in Perl 6.

NEW KEYWORDS

CLASS

A class is a cperl package with a readonly namespace, readonly @ISA and @DOES arrays for run-time inheritance and compile-time composition, and with compile-time optimized and type-checked fields and methods, subroutines and multi methods or subroutines, i.e. polymorphic dispatch with generics, dispatching on the method types.

It is different from an old package by being declared via the class keyword, and detected by the HvCLASS flag in the stash. Anonymous classes can only be created via the API, either using "class_role" in perlapi and "class_role_finalize" in perlapi, or setting HvCLASS_on(STASH) and HvROLE_on(STASH), or by adding a class and changing the name to a unique one. Dispatch and inheritance works on the classnames, not its pointers.

Classes can inherit from classes via a sequence of is CLASS traits or compose from other roles via does ROLE. Multiple such is or does traits are allowed.

The :native attribute or :repr(CStruct), :repr(CUnion) enforces natively typed access to the fields, with the same layout as in C. It is used to pass pointers or structs to and from C, e.g. via the ffi. (NYI)

ROLE

A role is a special class which can be compile-time composed into other classes or roles via does. All the fields, methods and subroutines are copied into the child class or role.

If a composed method or field already exists, an error is thrown.

Roles can inherit from classes via a sequence of is CLASS traits or compose from roles via does ROLE. Multiple such is or does traits are allowed.

    role MyRole { has $a }
    class MyClass does MyRole {}

    MyClass->new->a = 1; # use the a setter method from MyRole

METHOD

    method NAME (SIGNATURE) :ATTR BLOCK

The signature is optional and defaults to ($self). $self is added automatically to all methods as first signature argument. Using a different invocant than $self is possible via the invocant signature syntax $this:, a : suffix.

    method new ($this:, @inits) { bless [@inits], $this }

The declaration is similar to the old-style sub NAME (SIGNATURE) :method BLOCK declaration, just that with sub :method the $self argument is not automatically added.

Calling a method as subroutine is forbidden, and calling a subroutine with a signature as method is forbidden also. The new method keyword is only parsed inside a role or class. Outside it will try to call a subroutine method, which usually doesn't exist.

The special constructor methods new and CREATE are provided by Mu. Every method derives from Mu. So if a class doesn't provide a new method, Mu::new is used, which provides a typed array ref. If a class provides a new or CREATE method, the result must be typed to the given classname, i.e. bless [], $self;, and the fields layout must match the order of declaration, and the helper @FIELDS array and %FIELDS hash must be provided to resolve the field index and pad index of the field.

MULTI

A multi call polymorphically dispatches on all declared types of the method or subroutine argument to the best fit of all declared multi methods or subroutines. The dispatch strategy is left-to-right without backtracking, not smallest distance from all, as e.g. with perl6 or Class::MultiMethods.

    multi method NAME (SIGNATURE) :ATTR BLOCK
    multi sub NAME (SIGNATURE) :ATTR BLOCK
    multi NAME (SIGNATURE) :ATTR BLOCK

The new multi keyword is allowed inside and outside of roles or classes. The default $self argument is automatically added for multi methods, as with normal methods.

Internally a multi adds the signature types to the name after a \0. Multi dispatch is not yet implemented.

HAS

A class or role field is declared via has TYPE NAME = value;, with the type and the default value being optional, and the name having optional attributes, same as MY variable declarations. Like with method has is only parsed inside classes, outside it refers to a usually not-existing subroutine. Internally inside the class each has field is declared as lexical my variable, and then changed to efficient field accessors in all methods.

   has $NAME;
   has @ARRAY;
   has int $i = 0;
   has int $i :const = 0;

Each field generates an entry into an internal @FIELDS array, used to create the field accessors at compile-time, plus currently a %FIELDS hash entry for run-time access for computed field names, as in use fields. Native classes requires mandatory types for each field, the layout will be aligned the same as C structs. Fields are always compile-time composed, even with is CLASS inheritance. Note that @FIELDS and %FIELDS will be replaced by methods.

Additionally perl6-like default accessors are created, for non-const fields getters and lvalue setter methods, for const fields only getters. You may define you own accessor methods, overriding the defaults. There's no syntax for setter-only methods.

Inside a class $self->field is the same as $field. Outside a class the first type of accessor, the method must be used. If the name is computed or the field is inherited from a parent class the lookup will be dynamic via method inheritance, composed fields from does ROLE compile to direct array accesses in $self.

The compiler converts every typed $self method call on a field to a static compile-time array access via oelemfast, which is the same as aelemfast_lex_u, or to a new dynamic run-time oelem op, which performs the field lookup of the field value. It is not static when the class is open, or the object is not typed.

    my CLASS $obj = new CLASS; # explicit CLASS type for $obj
    $obj->field;           # -> $obj->[0] (via oelemfast $obj, index)

    my $field = 'field';   # computed field name -> dynamic
    $obj->$field;          # -> oelem $obj, $field

    my $obj = new CLASS; # type CLASS is inferred to my CLASS $obj
    $obj->field;         # -> $obj->[0] (via oelemfast $obj, index)

    class OPENCLASS :open {
      has $field = 1;
    }
    my OPENCLASS $obj = new OPENCLASS;
    $obj->field;        # calls field($obj)

Additionally, with a typed object the compiler only knows about the fields of the declared type. When you want to access a field in an untyped object , the compiler may not know it's position in the @FIELDS array at compile-time, so a slower run-time lookup has to be performed. Also an untyped object may change it's type at run-time, via unexpected side-effects. E.g. the object might get tied, re-blessed or a MOP method may be added.

    class MyBase {
      has $a = 1;
    }
    class MyClass is MyBase :open {
      has $b = 1;
    }

    my $o = new MyClass;
    # ... some possible side-effects with the type of $o
    $o->a;      # oelem $o, 'a';
    $o->b;      # oelem $o, 'b';

    my MyBase $o1 = new MyClass;
    $o1->b;     # compile-time error

    my MyClass $o2 = new MyClass;
    $o2->b;     # $o2->[1]  - $o2 is known to be MyClass

    $o = new MyClass;   # and directly afterwards:
    $o->a;      # $o2->[0]  - $o is known to be MyClass

LIMITATIONS

Max 65536 fields

Too many fields: cperl 5.27.2 added a limitation of max. 65536 fields per class.

field index fixups with roles

panic: cannot yet adjust field indices when composing role %s::%s into %s %s [cperl #311]"

When copying a method from a role to a class, and the field index from the role method would be different to a field index in the resulting class, the method is not yet fixed up to the new indices. A temp. solution would be to change the ordering of the roles, or to use the $self->field method syntax in the role method. This requires the not yet finished inliner. Currently we can only alias composed role methods and we don't change the ordering of the fields. See [cperl #311]

eval 'class {}' fails

Cannot be created in an eval block or subroutine. The pad lookup is still global and not per optional CvPADLIST.

Array and hash fields cannot be parsed yet.
B::class

B does not export the class method anymore.

  use B 'class';
  class $op

must now be converted to:

  use B;
  B::class $op
Performance

It is not yet the fastest field and method accessor, though theoretically it should be. It still has about the same speed as Mouse, though the new oref ops would allow to skip refcounting of fields and store them as native offsets into the object. Some oref compile-time optimizations are not yet implemented.