NAME

mop::manual::tutorials::moose_to_mop - A manual for p5-mop

DESCRIPTION

This tutorial is an attempt to map some of the common Moose concepts to their MOP equivalents.

Creating Classes

In Moose, a class is just a package which contains a use Moose declaration. After that, you use the Moose keywords: extends introduces inheritance, with adds roles, and has creates attributes in the class. After this, any subroutines created in the package are automatically added as methods of the class.

In the MOP there are explicit class, method, and attribute declarations. Here is a simple example:

class Foo extends Bar with Baz, Gorch {
    has $!foo;
    has $!bar;

    method foo { ... }
    method set_bar ($b) { ... }
}

This creates a class named Foo which is a subclass of the Bar class and consumes the Baz and Gorch roles. It contains two attributes, $!foo and $!bar, and two methods, foo and set_bar. set_bar takes one argument, $b.

The specific details of attributes and methods will be discussed later, but the above example should provide a basic overview of the base syntax.

Constructing instances

Classes which do not explicitly extend a superclass will automatically extend the mop::object class. This class will provide a new method which can be used to construct instances.

The new method inherited from mop::object accepts a set of key/value pairs where the key is the name of an attribute (minus the sigil). The instances created by the MOP are opaque, meaning they are not HASH refs, and access to the individual slots is only possible either inside a method (where they are accessible as normal variables) or via the MOP (the details of which are left as an exercise to the reader).

BUILDARGS

If you wish to override the default constructor's behavior with regard to arguments, then you simply create a new method yourself to accept different parameters, then munge those parameters into key/value pairs before calling the mop::object constructor with next::method.

This is very similar to overriding the BUILDARGS method in a Moose class. Here is a short example:

class Foo {
    has $!bar;

    method new ($class: $b) {
        $class->next::method( bar => $b );
    }
}

It should be noted that any attempt to assign to an attribute will result in an error. So if you either need access to $self or attributes, you can do this inside of the BUILD method, which will be automatically called from new.

BUILD

As with Moose, if you need to perform initialization on an instance, you can use the BUILD method for that.

Here is a simple example where the bar argument to the constructor is not actually stored, but instead is processed first, then stored in the $!double_bar attribute.

class Foo {
    has $!double_bar;

    method BUILD ($args) {
        $!double_bar = $args->{'bar'} * 2
            if exists $args->{'bar'};
    }
}

Creating Attributes

Below is the list of options for the Moose has keyword. With each of them we will show an example of how to accomplish the same with the MOP.

is => 'rw'|'ro'

This is supported by the core ro and rw traits, which are applied with the is modifier. Here is a simple example:

class Foo {
    has $!bar is ro;
    has $!baz is rw;
}
isa => $type_name

There is currently no support for types in the MOP. It is possible, however, to create a type trait that can perform the same validation, and even use Moose type constraints. See t/400-traits/003-type-trait.t for an example.

coerce => (1|0)

As was said with the isa option, there is no support for types in the MOP. However it would be possible to extend the type trait in t/400-traits/003-type-trait.t to also do coercion. It might look like this:

class Foo {
    has $!bar is type( isa => 'SomeType', coerce => 1 );
}

Actual implementation of this is left as an exercise for the reader.

does => $role_name

This option is really just a shortcut for a type assignment so the suggestions in the above two options apply here as well.

required => (1|0)

Required attributes are easily mimicked through the MOP without even needing a trait. Here is how they are accomplished:

class Foo {
    has $!bar = die '$!bar is required';
}

When an instance of Foo is created and no value is supplied for $!bar, then the default will be executed, which in this case will simply die with the error message.

weak_ref => (1|0)

Weak references are accomplished using the weak_ref trait that is core in the MOP. Here is a simple example:

class Tree {
    has $!parent is weak_ref;
}
lazy => (1|0)

Lazy attributes are supported via the core lazy trait. Here is a simple example of that:

class Foo {
    has $!bar is lazy;
}

Unlike Moose, where lazy attributes are only re-created if the slot has not been populated, in MOP they will be populated both if the slot has not been populated and if the value in the slot is undef. This is more in line with how regular scalars work, meaning that if you define a scalar with my $foo; then it is implicitly undef and not some special "not assigned to" value.

trigger => $code

There is no specific way to do trigger yet, but take a look at t/400-traits/020-moose-traits.t for a naive (and mostly wrong) version.

handles => ARRAY | HASH | REGEXP | ROLE | ROLETYPE | DUCKTYPE | CODE

There is no specific way to do handles yet, but take a look at t/400-traits/020-moose-traits.t for a simple version that supports the basic HASH syntax.

NOTE: This will very likely be put into the core traits, but for now it is not.

traits => [ @role_names ]

This is not currently supported, and honestly, probably never will be, since it is highly Moose specific.

builder => Str

There is no special trait for builder. Instead, you simply use the existing syntax for assigning default values to attributes and just call a method. Here is an example:

class Foo {
    has $!bar = $_->build_bar;

    method build_bar { ... }
}

The standard perl topic variable ($_) is localized to be the current instance when running the builders, so you can use it to access the current invocant.

default => SCALAR | CODE

There is also no special trait for default. Instead you use the existing syntax. Here is how you would provide a simple default value:

class Foo {
    has $!bar = 10;
    has $!baz = {};
}

Pretty much anything you can stick in a scalar variable can go on the right hand side of a has expression.

If you need to initialize a more complex value, but for some reason do not want to use the builder style approach, you can wrap your default value in a do block, like so:

class Foo {
    has $!bar = do {
        $_->bar_has_been_touched;
        100;
    };
}

While this works, most often it is better to use the builder approach.

clearer => Str

There is no special trait for clearer. Instead it is recommended that you write a simple method that follows this pattern instead.

class Foo {
    has $!bar;

    method clear_bar { undef $!bar }
}

If $!bar was lazy, this would force a recalculation of $!bar the next time that $!bar was accessed.

predicate => Str

There is no special trait for predicate. Instead it is recommended that you write a simple method that follows this pattern instead.

class Foo {
    has $!bar;

    method has_bar { defined $!bar }
}

It should be noted that if $!bar was lazy, this would force evaluation of $!bar. If you want to test if a lazy value is yet to be initialized, you need to go through the MOP to get that. Here is what that code would look like.

mop::meta($self)->get_attribute('$!bar')->has_data_in_slot_for($self)

The real lesson here is that if you want lazy and predicates, you should implement the accessors yourself. See t/001-examples/003-binary-tree.t for an example of that.

documentation => $string

This is not currently supported in the MOP and likely won't be. To the best of my knowledge it was hardly ever actually used in Moose.

BUGS

Since this module is still under development we would prefer to not use the RT bug queue and instead use the built in issue tracker on Github.

Git Repository

Issue Tracker

AUTHOR

Stevan Little <stevan.little@iinteractive.com>

Jesse Luehrs <doy@tozt.net>

COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by Infinity Interactive.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.