In [our] journey since the last two posts, we've covered a bit of introductory Perl 6 ranging from a scalar to currying functions and other tom foolery. Here, we'll discuss a big part of Perl 6: Object oriented programming.
Anybody who comes from a Perl 5 background probably knew that Object-orientation wasn't exactly first class; I don't even know if you'd define it as 'coach.' Perl 6 now extends this to full object-orientation. -Real- object-orientation, if you wish to think of it in that manner.
That means this will most likely be long ride. So, we should probably get on soon as possible.
Classes
Class declaration comes in two forms:
class Foo; #rest of file is class definition
class Bar { ... }; #block is class definition
The first can only be used as the first declaration [statement] in the file or string (in the case of eval
.)Attributes
Attributes are known in other cultures as instance variables of a
class
. They are declared with the has
keyword, and declared public with a "." sigil following the primary sigil (see below); this generates accessor methods in the class of the same name. Private attributes that indicate no accessor method are declared with the secondary "!" sigil. Ex:pugs> class Dog {
....> has $.name is rw = 'bruce';
....> has $.age is rw = 6;
....> has @.legs is rw;
....> has $!brain;
....> }
undef
pugs> my $d = Dog.new();
pugs> $d.name
"bruce"
pugs> $d.age()
6
pugs> $d.age = 7
7
pugs> $d.brain()
*** No such method in class Dog: "&brain"
at line 1, column 1-11
pugs>
MethodsMethods are a lot like subroutines, but with their own keyword
method
. The obvious difference is they're invoked on objects. In Perl 6, many of the primitive types are objects like Int
and String
. That is how we invoked methods such as .say();
Ex:pugs> class Bar {
....> has $.name is rw = 'jack';
....>
....> method h {
....> say "hello world";
....> }
....> method myname {
....> say "my name is "~$.name;
....> }
....> }
pugs> my $me = Bar.new;
pugs> $me.name = 'austin';
pugs> $me.myname;
my name is austin
pugs> $me.h();
hello world
pugs> h($me:);
hello world
The last way is an indirect object call. It acts like a subroutine, with the object seperated from the parameters. The parameters are comma seperated after the colon, i.e.: doit($object: 1,"world");
And of course, the parenthesis are optional.
Inheritance
Classes can inherit from one another using the
is
keyword. That is, the act of saying a class inherits from another is just a class trait. Ex:class Hello is World { ... }
class Cool is Awesome is Fun { ... } #multiple inheritence
You can access inherited attributes just like normal:pugs> class Foo {
....> has $.name = 'jack';
....> }
....>
....> class Bar is Foo {
....> method z {
....> say $.name;
....> }
....> }
pugs> my $x = Bar.new()
pugs> $x.z
jack
Bool::True
pugs>
RolesA
role
is a reusable unit of class code. In the way that modules export subroutines and the like for other modules and your code, a role exports methods and attributes into a class. This is like inheritence; inheritence is just a different way to reuse code.A role define's both attributes and methods, i.e.
pugs> role Hi {
....> has $!status;
....> has $.world = 'earth';
....> method initstatus($s) {
....> $!status = $s;
....> }
....> method printstatus {
....> say $!status;
....> }
....> }
pugs>
They can be pulled into a class as such:pugs> class Foobar does Hi { ... }
When you use roles with a class, they are treated almost as if you had simply typed in that code right there. With inheritence you only have accessor methods to access your attributes; with role's they can be referenced by their $.name
(this applies to private attributes as well.) Ex:pugs> class Foobar does Hi {
....> has $.name = 'austin';
....> method sayname {
....> say $.name;
....> }
....> }
We pull in the other methods through the role. Continuing:pugs> my $x = Foobar.new();
pugs> $x.name
"austin"
pugs> $x.world
"earth"
pugs> $x.initstatus("super");
pugs> $x.printstatus;
super
pugs>
Using Roles, it is as if we wrote the code in Hi
directly in the declaration of class Foobar
. This is reusable, and it can be used to give methods to classes that need similar functionality, but without an inheritence relationship.An interface is a role that only defines method stubs, and no attributes.
Role Conflicts
Given one class defined over two roles, each with a method of the same name, which method should the class take when it's invoked? Either. In Perl 6, you simply choose one, and you can do it by giving a full name of the method you want to call (the format is
Role::method
) given the roles your class uses.Delegation
Delegation lets you act like another object's methods are your own methods. You use the
handles
keyword to specify what methods will act on what attributes rather than on the object itself. It sounds confusing, but it's easy in practice and gives you some nice reusable code. Ex:pugs> class backend1 {
....> method hello { say "hi" }
....> }
pugs> class backend2 {
....> method hello { say "hello" }
....> }
pugs> class Foo is backend1 is backend2 {
....> has $.backend handles "hello";
....> }
undef
pugs> my $f = Foo.new();
pugs> $f.backend = backend1.new();
pugs> $f.hello;
hi
pugs>
As you see, if you use the handles
keyword with an attribute, by setting that attribute to an object you can delegate methods to that object rather than yours.Scope (private/public)
In a class, methods are implicitly public. You can make them private simply by appending
my
to the method
keyword.There is a loophole in private scope like this however; a class can declare it
trusts
another class to access it's private methods:pugs> class Bug {
....> trusts Zapper; # probably a bad idea
....> }
SubroutinesSubroutines can be defined in classes just like methods. They cannot be accessed via
$object.methodname
invocation, and cannot be inherited although you can pull them in by roles.Submethods
"Submethods are for declaring infrastructural methods that shouldn't be inherited by subclasses, such as initializers." (S12) Ex:
pugs> class A {
....> method foo { say "foo" }
....> }
pugs> class B is A {
....> submethod foo { say "foobar" }
....> }
pugs> class C is B is A { ... }
pugs> my $b = B.new;
pugs> my $c = C.new;
pugs> $b.foo;
foobar
pugs> $c.foo;
foo
pugs>
We can do this thanks to the joys of multiple inheritance via multiple
is
traits.Multiple dispatches and Operator overloading
In the last article we discussed
multi
subroutines that applied to having two subroutines of the same name, with different signatures. multi
applies to any code object like a sub
, method
or submethod
. Ex:pugs> class Foo {
....> multi method hello(Int $i) { say "int:\t"~$i }
....> multi method hello(Str $s) { say "str:\t"~$s }
....> }
pugs> my $f = Foo.new();
pugs> $f.hello(1);
int: 1
pugs> $f.hello("austin");
str: austin
pugs>
Operator overloading make use of multiple dispatch. Operators are just subs with a special syntax that describes how they are used in an expression. You can define an operator as prefix
, postfix
(unary), or infix
(binary.)Operators are defined with subs in the form of
multi sub how:exp
.how
is that of the above (prefix postfix
and infix
), while exp defines the operator to overload, using any method to treat it as a hash subscript, i.e. you could use <+>, <<+>>, {'+'}
, etc..Ex:
pugs> class Foo {
....> has $.age = 10;
....> }
undef
pugs> multi sub *infix:<+> (Foo $a, Foo $b) { $a.getage + $b.getage }
pugs> my $a = Foo.new;
pugs> my $b = Foo.new;
pugs> $a + $b
20
pugs>
The * at the start of the multi-sub definition indicates that it is in global scope; without this, the operation of addition is lexically scoped to the area at which it was defined. Generally, you'll probably want to use *, though. As of pugs r16643, operator overloading in this fashion does not seem to work yet.Conclusion
This time we looked at a very big part of Perl 6. Just by using simple features like Roles and Delegations in Perl 6, you can achieve some very powerful constructs. You also get all the classic object-oriented features you'd expect from other languages.
Have fun. :)
Next round: Grammars + Rules.