Introduction to Moose


Published on

Moose is a framework for Perl 5 build on top of Class::MOP, that brings all features of object-oriented programming.

Published in: Technology
1 Comment
No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Introduction to Moose

  1. 1. MooseOOP for Perl.
  2. 2. What we expect from OOP● Classes (methods, types, attributes, open recursion...)● Encapsulation● Aggregation● Message passing and dynamic dispatch● Inheritance and polymorphism● Abstraction Skip definitions
  3. 3. Classes● Templates for defining objects state and behavior
  4. 4. Encapsulation● Specifying which classes may use the members of an object e.g: public, protected or private, determining whether they are available to all classes, sub-classes or only the defining class.
  5. 5. Aggregation● Object containing another object
  6. 6. Message passing and dynamic dispatch● Message passing is the process by which an object sends data to another object or asks the other object to invoke a method● when a method is invoked on an object, the object itself determines what code gets executed by looking up the method at run time in a table associated with the object (needed when multiple classes contain different implementations of the same method )
  7. 7. Inheritance and polymorphism● allows the programmer to treat derived class members just like their parent classs members ("Subclasses" are more specialized versions of a class, which inherit attributes and behaviors from their parent classes, and can introduce their own.)● a class has all the state and behavior of another class - polymorphism
  8. 8. What OOP features offers Perl?● MOP in Perl5: ● Class - package ● Object - blessed reference (a scalar type holding a reference to object data – object may be a hash, array, or reference to subroutine.) ● Method - a subroutine within a package ● Constructor – a method that returns reference to blessed variable
  9. 9. What OOP features offers Perl?package Car; sub new { my $class = shift; my $brand = shift; my $self = {}; $self->{brand} = $brand; bless ($self, $class); return $self; }
  10. 10. What OOP features offers Perl?● Encapsulation – in general only by convention.● Some tricks: private variable, private method, private function package Mom; package Kido; sub new{ use base qw(Mom); my $class = shift; my $self = { sub ask_for_pin{ CARD_PIN => 0000, my $self = shift; }; my $how = shift; return &{$self}($how); my $closure = sub { } my $arg = shift; if ($arg and $arg eq nice){ return $self->{CARD_PIN}; package main; } my $mom = Mom->new(); else { my $kido = Kido->new(); return forget!; } print $kido->ask_for_pin(not nice); }; print $kido->ask_for_pin(nice); bless($closure, $class); return $closure; };● “Inside-out” classes not handy... “There is only one way to do it...” .
  11. 11. What OOP features offers Perl? {{ package Mom;package Mom; (...) (...) sub _prepare_pastry{ my $_prepare_pastry = sub{ if (caller ne __PACKAGE__){ print "makin pastry 2 n"; die; }; } else { sub make_cookies{ print "makin pastry n"; &{$_pastry}; } print "cookies readyn"; } }} sub make_cookies{ _prepare_pastry();package main; print "cookies readyn";(...) }package main; }my $mom = Mom->new(); package main;$mom->make_cookies(); (...) my $mom = Mom->new(); $mom->_prepare_pastry(); #this compiles! $mom->make_cookies();
  12. 12. What OOP features offers Perl?● Aggregation package Person; my $eva = Person->new(); my $car1 = Car->new(); sub new { $car1->brand( Fiat ); my $class = shift; my $self = {}; $eva->cars->[0]->brand(); #returns Fiat $self->{cars} = (); bless( $self, $class ); return $self; } sub cars { my $self = shift; my $car = shift; if ( ref( $car ) eq Car ) { push @{ $self->{cars} }, $car; } return $self->cars; }
  13. 13. What OOP features offers Perl?● Inheritance (multi-object) and polymorphism: use base()package Person; package Employee;sub new { use base qw(Person); my $class = shift; my $self = {}; sub new { my $class = shift; bless( $self, $class ); my $job = shift; return $self; my $self = $class->SUPER::new();} $self->{job} = $job;sub name { bless( $self, $class ); my $self = shift; return $self; if ( @_ ) { $self->{name} = shift } } return $self->{name};} #overriding sub say_name {sub say_name { my $self = shift; my $self = shift; $self->SUPER::say_name(); printf( "My name is %s. n", $self->{name} ); printf( "My job is %s.n", $self->{job} );} } #inheritance and polymorphism my $adam = Employee->new( Developer ); $adam->name( Adam ); $adam->say_name
  14. 14. What OOP features offers Perl?● Abstraction – only objects package File; my $bmp = BMP->new(); use Carp; $bmp->load(); sub new { my $jpg = JPG->new(); my $class = shift; $jpg->load(); my $self = {}; bless ($self, $class); return $self; } sub load{ croak(not implemented); } package BMP; use base qw(File); package JPG; use base qw(File); sub load{ my $self = shift; print "File loaded. n"; }
  15. 15. What OOP features offers Perl?What is missing?● The in-language support for OO features is minimal.● No type checking, no horizontal inheritance, no encapsulation... There is more than one way to do it!
  16. 16. MooseThere is more than one way to do it, but sometimes consistency is not a bad thing either!
  17. 17. Moose is Perl. Moose =Class::MOP + semantic
  18. 18. Moose is Perl.● common system for building classes● new levels of code reuse, allowing you to improve yourcode with features that would otherwise be too complex orexpensive to implement
  19. 19. Moose is Perl.Everything missing in OO Perl you can find in Moose + someimportant bonuses! ● Type matching ● Coercion - the ability to convert types of one type to another when necessary ● Encapsulation ● Method Modifiers - the ability to add code that runs before, after or around an existing method ● Horizontal inheritance - roles - the ability to add predefined functionality to classes without sub-classing… and much more.
  20. 20. Moose – first example. Sets strict and warnings package Vehicle; use Moose;MOOSE CLASS has RegNum => ( Class attributes. is => rw, isa => Str); sub goto{ my $self = shift; Class methods. my $place_name = shift; print "Going to $place_name n"; } __PACKAGE__->meta->make_immutable; no Moose; 1; use Vehicle; We get new() for free my $avehicle = Vehicle->new(); $acar->RegNum(123456); print $acar->RegNum; We get also default $acar->goto(Oriente); accessors for free.
  21. 21. Moose – first example.12 lines of code from previous slide give us all this: package Vehicle; use strict; sub new { my $class = shift; my %args = @_; my $self = { _RegNum => undef }; if ( exists( $args{RegNum} ) && defined( $args{RegNum} ) ) { $self->{_RegNum} = $args{RegNum}; } return bless($self, $class); } package Vehicle; use Moose; sub RegNum { my $self = shift; has RegNum => ( my $regnum = shift; is => rw, if ( $regnum ) { isa => Str); $self->{_RegNum} = $regnum; } sub goto{ return $self->{_RegNum}; my $self = shift; } my $place_name = shift; print "Going to $place_name n"; sub goto{ } my $self = shift; my $place_name = shift; no Moose; print "Going to $place_name n"; 1; } 1;
  22. 22. Lets have a closer look...● Arguments (...) rw or ro – tells which accessors will be created has RegNum => ( is => rw, isa => Str ); Moose types: (...) Bool, Str, Num, Int, ArrayRef, HashRef, and moreis – defines if argument is read-write or read-onlyisa – defines type of the attribute (and validate it during assignment)
  23. 23. Lets have a closer look...● Read- only arguments has RegNum => ( If we restrict access to the attribute... is => ro, the default accessor wont be created. isa => Str ); This method doesnt exist $avehicle->RegNum(123456); – it results in compilation error The only right moment to set up the my $avehicle = Vehicle->new( attribute is during initialization. RegNum=>1234 ); We can still read it wherever we want. print $avehicle->RegNum."n";
  24. 24. What more we can do with attributes?● Required arguments Required attribute must be set. It means that...has RegNum => ( is => ro, isa => Str, required => 1, ); … this will not compile#$acar = Vehicule->new();my $avehicle = Vehicle->new(RegNum=>123456); ...but we still can have illegal vehicle.. RegNum can be set to empty $avehicle = Vehicle->new(RegNum=>);has RegNum => ( is => ro, required => 1, ); BUTmy $avehicle = Vehicle->new(RegNum=>undef); If attribute has no type it can be set to undefined
  25. 25. What more we can do with attributes?●Clearer and predicateImagine... the vehicle is a broken and is waiting to be scraped, but first you must unregister it .has RegNum => ( is => ro, You cant use accessors because it is “ro”. isa => Str, required=> 1, clearer => unregister, but... you can set clearer to clean the value predicate=>is_registered, ); and predicate to check if the value existsmy $avehicle = Vehicule->new(RegNum=>1234);print $avehicle->RegNum;$avehicle->unregister();if (!$avehicle->is_registered()){ The method you get for free. print "Vehicle illegal!.n"; }
  26. 26. What more we can do with attributes? ● SubtypesNot every string can be the registration number... package Vehicle; use TypeConstraints use Moose; and create subtype use Moose::Util::TypeConstraints; subtype RegistrationNo => as Str, parent type => where { /^(dd)(-)(ww)(-)(dd)$/ }, => message {"$_ is not valid registration number."}; has RegNum => ( compare $_ to something is => ro, isa => RegistrationNo, required=> 1, clearer => unregister, and use it predicate=>is_registered, );my $avehicle = Vehicle->new(RegNum=>11-xx-22); Subtypes can be more complex more examples here#my $avehicle = Vehicle->new(RegNum=>111-xx-22);
  27. 27. One Step Further - MooseX. ● Custom typesLets define where is our vehicle.... a 2D coordinate - two points: x and y thatmust have positive value.package MyTypes; what we want to createuse Moose;use MooseX::Types -declare => [qw(Coordinate Point)];use MooseX::Types::Moose qw(Int HashRef); what we use to createsubtype Point, as Int, a point where { $_>= 0}, message {Must be positive number.}; A coordinate that is a has of two valid pointssubtype Coordinate, as HashRef, where { Point->check( $_->{X} ) and Point->check( $_->{Y} )}, message {Incorect coordinate..$_ }; This is very useful type. We can pack it in separate package and reuse later.
  28. 28. One Step Further - MooseX. ●Custom typesLets use the coordinate to define where is the vehicle. use MyTypes qw( Coordinate ); import custom type (...) has place => ( modify the vehicle class is => rw, isa => Coordinate, ( ); (...) my $place = {X=>1, Y=>3 }; $avehicle->place( $place ); use it where ever you need
  29. 29. One Step Further - MooseX. ● Custom types and coercion In the end, in class definition,But maybe it would be easier to use it this way...? Dont forget to turn on the coercion on the argument $avehicle->place( [4,5] );package MyTypes;use Moose;use MooseX::Types -declare => [qw(Coordinate Point)];use MooseX::Types::Moose qw(Int HashRef ArrayRef);subtype Point, as Int, (...) where { $_> 0}, has place => ( message {Must be positive number.}; is => rw, isa => Coordinate, coerce => 1,subtype Coordinate, ); as HashRef, where { Point->check( $_->{X} ) and Point->check( $_->{Y} )}, message {Incorect coordinate..$_ }; (...)coerce Coordinate, from ArrayRef, via { {X=>$_->[0], Y=>$_->[1]} }; We define how we can transform one type to another.1;
  30. 30. Private attributes – only convention. The vehicle may change its place only when it moves ?(...) This is what the manual says#an attribute to be publicly readable, but only privately settable. – but this is only CONVENTION.has _place => ( is => ro, isa => Coordinate, coerce => 1, writer => _set_place, ); reader and writer redefine the names of custom accessorssub goto{ my $self = shift; my $coordinate = shift; $self->_set_place($coordinate);} Now we can “move” the vehicle.(...)$avehicle->goto([7,8]);print Dumper($avehicle);print The vehicle is at.$avehicle->_place->{X}.,.$avehicle->_place->{Y}..;#my $place = {X=>1, Y=>3 }; However this still would work.#my $other_place = [4,5]; MOOSE is PERL#$avehicle->_set_place( $place );
  31. 31. Method modifiersMileage – every time the vehicle moves its mileage increase. has _mileage => ( One more is => ro, isa => Num, private attribute. required => 1, default => 0, writer => _set_mileage, ); Method modifier Has access to attributes, around goto => sub { before they are modified by my $orig = shift; my $self = shift; method my $coordinate1 = $self->_place; my $x1 = $coordinate1->{X}; my $y1 = $coordinate1->{Y}; Executing main method #@_ stores new coordinate $self->$orig(@_); my $coordinate2 = $self->_place; my $x2 = $coordinate2->{X}; Accessing modified my $y2 = $coordinate2->{Y}; attributes my $distance = sqrt(($x1-$x2)**2+($y1-$y2)**2); $self->_set_mileage($self->_mileage + $distance); other actions };
  32. 32. Method modifiersMileage – every time the vehicle moves its mileage increase. Define default value has _place => ( is => ro, isa => Coordinate, coerce => 1, writer => _set_place, default => sub {[0,0]}, ); print $avehicle->_mileage; Every time we move vehicle $avehicle->goto([7,8]); $avehicle->goto([1,1]); the mileage increase. $avehicle->goto([4,5]); print $avehicle->_mileage
  33. 33. Lazy, lazy_build and trigger.Imagine we want to sell the vehicle. The price depends on the mileage. Lazy - value is calculated only when has price => ( is => ro, it is accessed and is unset. isa => Num, predicate=>has_price, lazy_build=>1, builder=>calculate_price, lazy + clearer = lazy_build init_arg=>undef, ); sub calculate_price { Lazy needs default value or builder my $self = shift; my $price = 100000; if ($self->_mileage != 0){ $price = $price / $self->_mileage; } return $price; Method building the value. } $avehicle->goto([1,1]); ...despite we move, the price didnt change. print $avehicle->price; Lazy build calculates the value only if it is unset. $avehicle->goto([4,5]); Therefore we need to clean it every time we want to get updated value. print $avehicle->price;
  34. 34. Lazy, lazy_build and trigger.Lets run the clearer automatically. has _mileage => ( Every time the mileage changes the is => ro, clearer for price attribute is triggered. isa => Num, required => 1, default => 0, writer => _set_mileage, trigger => sub { my $self=shift; After this change the previous example $self->clear_price; shows updated value every time we access it } );Lazy build is very useful for attributes are expensive to calculate –we calculate it only while reading. sub sell_vehicle { my $self = shift; $self->clear_price; return $self->price; We can also use method instead the trigger. } The price is updated every time we read it $avehicle->goto([1,1]); using the method. print $avehicle->price; Useful for expensive calculation. $avehicle->goto([4,5]); print $avehicle->price; print "The final price: ".$avehicle->sell_vehicle. "n";
  35. 35. Code reuse - inheritance in Moose.Is the vehicle car or bike?package Car;use Moose; Parent classextends Vehicle;has fuel =>( is => rw, isa => Int, default=>0, New attribute);sub refuel { my $self = shift; $self->fuel($self->fuel + 10); New method }override goto => sub{ my $self = shift; Overwritten method if($self->fuel eq 0){ print No fuel; return; } return super;}; Calling superno Moose
  36. 36. Code reuse - inheritance in Moose.package Bike;use Moose;extends Vehicle;has +RegNum => ( required=> 0, ); changing inherited attributeno Moose;1;my $abike = Bike->new(); registration number is not linger required
  37. 37. Better code reuse – inheritance and roles.Short explanation:● Role is a “state or behavior that can be shared between classes.”● Role is not a class – cant be subclassed, cant be extended … we cant create an object of a role .● But we can use a role in a class, and methods of the role in an object.
  38. 38. Better code reuse – inheritance and roles.We can create a truck role and use it in our vehicle. The only difference package Truck; sub do_load { use Moose::Role; my $self = shift; my $load = shift; has capacity => ( is => ro, if(($self->capacity >= $load) and !$self->load){ isa => Int, $self->load(1); default=>10, }else{ required=>1, print Load unsuccessful,; ); } } has load => ( sub do_unload { is => rw, my $self = shift; isa => Bool, my $load = shift; default=>0, ); if($self->load){ $self->load(0); }else{ print Nothing to unload,; } }We create a piece of functionality no Moose; 1;exactly the same way as we were creating a moose class.
  39. 39. Better code reuse – inheritance and roles.We want to have a car with a “truck functionality” - a lorry. package Lorry; use Moose; extends Car; with Truck; Lorry = Car with Truck role no Moose; 1; How to use Lorry? Lorry is a vehicle – mast have registration number. (…) use Lorry; (…) Lorry has a Truck role – mast have capacity. my $alorry = Lorry->new(RegNum=>11-xx-23, capacity=>50); #$alorry->do_load(100); - more than capacity $alorry->do_load(40); #$alorry->do_load(40); #unsecessfull - is loaded We can use all truck functions. $alorry->do_unload(); $alorry->do_load(30); #$alorry->goto([2,4]); - still cant go without the fuel But Lorry is still a car so wont go without fuel.
  40. 40. Better code reuse – inheritance and roles.In the end we want to assure that the truck role will be only used with anything that uses fuel(Have you ever seen a lorry-bike?) package Truck; use Moose::Role; We require that a method fuel requires fuel; is present in a class where we use the Truck role. (…) use Moose; extends Bike; with Truck; no Moose; Since bike doesnt use fuel this code will not compile. Truck requires the method fuel to be implemented by Lorrybike at /usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi/Moose/Meta/Role/ line 51 Moose::Meta::Role::Application::apply(Moose::Meta::Role::Application::ToClass=HASH(0xa5faa2c), Moose::Meta::Role=HASH(0xa5f6a6c), Moose::Meta::Class=HASH(0xa5f8754)) called at /usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi/Moose/Meta/Role/Application/ line 31 (...)
  41. 41. BUILDARGS and BUILDLets go back to Vehicle. It would be awesome to create a vehicle this way : $acar = Vehicule->new(11-xx-11); BUILDARGS method is called as a class method around BUILDARGS => sub { before an object is created. It has direct access my $orig = shift; to arguments passed in method call. my $class = shift; my %a = @_; my %args = (); if ( @_ == 1 ) { $args{ RegNum } = $_[0]; return $class->$orig( %args ); } return $class->$orig( %a ); };
  42. 42. BUILDARGS and BUILDThere is a possibility to have a bike without registration. But we want to be warn about it... BUILD method is called after an object is created. sub BUILD { my $self = shift; if(!$self->RegNum){ if($self->isa(Bike)){ print "Vehicle without registration number.n"; } } } We will be warn about creating a bike without a registration number. my $abike = Bike->new(); my $abike2 = Bike->new(22-yy-66);
  43. 43. Aggregation and Native TraitsLets create a garage for our vehicles. package Garage; Notice: there is no use Moose; use Moose::Util::TypeConstraints; is =>ro|rw has vehicles => ( isa=> ArrayRef[ Vehicle ], default => sub {[]}, traits => [Array], handles=> { Trait – a role added to attribute. add_vehicle => push, Here we make the vehicles attribute get_vehicle => shift, work as an array. }, ); no Moose; 1; Defining accessors. (these accessors use type checking).More native traits:Array: push, pop, shift, unshit, map, sort, grep …Hash: get, set, delete, exists, defined, clear, is_empty …String: append, substr, length...Bool: set, unset, toggle...Code: execute, execute_method...
  44. 44. Aggregation and Native TraitsLets park some vehicles... my $garage = Garage->new(); $garage->add_vehicle($avehicle); my $v = $garage->get_vehicle(); $garage->add_vehicle($acar); my $acar2 = $garage->get_vehicle(); $acar2 and $acar is the same (reference to the same object)
  45. 45. What more you can do in Moose?For example:● Delegation - "proxy" methods call some other method on an attribute.● Custom traits - MooseX● Custom OO system – sub-classing methaclasses● And much more...
  46. 46. Some tips● If you dont know what is wrong try to load package from command line : perl -Ilib/ -e use Coordinate● $object->dump for debugging
  47. 47. More information:● Moose is Perl: A Guide to the New Revolution●Awesome presentation with huge dose of humor, but also very helpful.●Moose::Manual and Moose::Cookbook