Friday, June 8, 2007

Perl 6: Round 3

Pugs revision: r16643

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>

Methods
Methods 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>
Roles
A 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
....> }
Subroutines
Subroutines 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.