Wed, 24 Sep 2008

Objects and Classes


Permanent link

NAME

"Perl 5 to 6" Lesson 05 - Objects and Classes

SYNOPSIS

    class Shape {
        method area { ... }    # literal '...'
        has $.colour is rw;
    }

    class Rectangle is Shape {
        has $.width;
        has $.height;

        method area {
            $!width * $!height;
        }
    }

    my $x = Rectangle.new(
            width   => 30.0,
            height  => 20.0,
            colour  => 'black',
        );
    say $x.area;                # 600
    say $x.colour;              # black
    $x.colour = 'blue';

DESCRIPTION

Perl 6 has an object model that is much more fleshed out than the Perl 5 one. It has keywords for creating classes, roles, attributes and methods, and has encapsulated private attributes and methods. In fact it's much closer to the Moose Perl 5 module (which was inspired by the Perl 6 object system).

There are two ways to declare classes

    class ClassName;
    # class definition goes here

The first one begins with class ClassName; and stretches to the end of the file. In the second one the class name is followed by a block, and all that is inside the block is considered to be the class definition.

    class YourClass {
        # class definition goes here
    }
    # more classes or other code here

Methods

Methods are declared with the method keyword. Inside the method you can use the term self to refer to the object on which the method is called (the invocant).

You can also give the invocant a different name by adding a first parameter to the signature list and append a colon : to it.

Public methods can be called with the syntax $object.method if it takes no arguments, and $object.method(@args) or $object.method: @args if it takes arguments.

    class SomeClass {
        # these two methods do nothing but return the invocant
        method foo {
            return self;
        }
        method bar(SomeClass $s: ) {
            return $s;
        }
    }
    my SomeClass $x .= new;
    $x.foo.bar                      # same as $x

(The my SomeClass $x .= new is actually a shorthand for my SomeClass $x = SomeClass.new. It works because the type declaration fills the variable with a "typo object" of SomeClass, which is an object representing the class.)

Methods can also take additional arguments just like subs.

Private methods can be declared with method !methodname, and called with self!method_name.

    class Foo {
        method !private($frob) {
            return "Frobbed $frob";
        }

        method public {
            say self!private("foo");
        }
    }

Private methods can't be called from outside the class.

Attributes

Attributes are declared with the has keyword, and have a "twigil", that is a special character after the sigil. For private attributes that's a bang !, for public attributes it's the dot .. Public attributes are just private attributes with a public accessor. So if you want to modify the attribute, you need to use the ! sigil to access the actual attribute, and not the accessor (unless the accessor is marked is rw).

    class SomeClass {
        has $!a;
        has $.b;
        has $.c is rw;

        method set_stuff {
            $!a = 1;    # ok, writing to attribute from within the clas
            $!b = 2;    # same
            $.b = 3;    # ERROR, can't write to ro-accessor
        }

        method do_stuff {
            # you can use the private name instead of the public one
            # $!b and $.b are really the same thing
            return $!a + $!b + $!c;
        }
    }
    my $x = SomeClass.new;
    say $x.a;       # ERROR!
    say $x.b;       # ok
    $x.b = 2;       # ERROR!
    $x.c = 3;       # ok

Inheritance

Inheritance is done through an is trait.

    class Foo is Bar { 
        # class Foo inherits from class Bar
        ...
    }

All the usual inheritance rules apply - methods are first looked up on the direct type, and if that fails, on the parent class (recursively). Likewise the type of a child class is conforming to that of a parent class:

        class Bar { }
        class Foo is Bar { }
        my Bar $x = Foo.new();   # ok, since Foo ~~ Bar

In this example the type of $x is Bar, and it is allowed to assign an object of type Foo to it, because "every Foo is a Bar".

Classes can inherit from multiple other classes:

    class ArrayHash is Hash is Array { 
        ...
    }

Though multiple inheritance also comes with multiple problems, and people usually advise against it. Roles are often a safer choice.

Roles and Composition

In general the world isn't hierarchical, and thus sometimes it's hard to press everything into an inheritance hierarchy. Which is one of the reasons why Perl 6 has Roles. Roles are quite similar to classes, except you can't create objects directly from them, and that composition of multiple roles with the same method names generate conflicts, instead of silently resolving to one of them, like multiple inheritance does.

While classes are intended primarily for type conformance, roles are the primary means for code reuse in Perl 6.

    role Paintable {
        has $.colour is rw;
        method paint { ... }
    }
    class Shape {
        method area { ... }
    }

    class Rectangle is Shape does Paintable {
        has $.width;
        has $.height;
        method area {
            $!width * $!height;
        }
        method paint() {
            for 1..$.height {
                say 'x' x $.width;
            }
        }
    }

    Rectangle.new(width => 8, height => 3).paint;

SEE ALSO

http://perlcabal.org/syn/S12.html http://perlcabal.org/syn/S14.html http://www.jnthn.net/papers/2009-yapc-eu-roles-slides.pdf http://en.wikipedia.org/wiki/Perl_6#Roles

[/perl-5-to-6] Permanent link

Comments / Trackbacks:

Trackback URL: /blog-en.trackback

Write a comment

 
Name:
URL: [http://www.example.com/] (optional)
Title: (optional)
Comments:
Save my Name and URL/Email for next time