perl object ?

32
Perl Object ? SLC.pm October 2015 Perl Object Nicolas Rochelemagne

Upload: nicolas-rochelemagne

Post on 14-Jan-2017

208 views

Category:

Software


0 download

TRANSCRIPT

Perl Object ?

SLC .pm October 2015 Perl Object Nicolas Rochelemagne

Perl Object ?

• Why ?

• When ?

• How ?

Without objectswe manipulate scalars, arrays, hashes...$hash = {         key => 42,         another => {                 one  => 43,                 deep => {                         here => {                                 you => {                                         can => 'find me'                                 }                         }                 },                 str  => "Data value"         },         elts => [ 8, 25, 37, 64 ] };

this lead to store data in /ugly/ hashes

Problems with this approach :you need to know the "exact key" to use...

$hash->{key} = 42; $hash->{anotherlevel}->{key} = "value"; $hash->{where}->{can}{be}{mykey} = { it => 's there' };

deep research into the code...or need to dump huge hash

You can s/confound/mispell/ key namesomewhere

sub method {         my $h = shift;         $h->{key} = 42; }

then later...         $h->{keys}++ and $h->{kyes} = 51; ...

You cannot change the key nameor storage level

cannot become neither $hash->{current_key} = 42;

nor

$hash->{updated_key} = 42;

$hash->{better}{key} = 42;

without updating the full code !

You cannot control life cycle of an attribute :

sub somewhere {         ...         $hash->{mykey} = 42 and additionnal_operation;         ... }

sub otherplace {         ...         $hash->{mykey} = 43 and additionnal_operation;         ... }

•on create •on update•before, after, around•hooks : innner, augment...

without objects

You would need an accessor

it should be the only single wayto access to this piece of data

sub mykey {         my ($self, $value) = @_;

        if (defined $value) {                 $self->{_mykey} = $value;                 $self->additionnal_operation();         }                 $self->{_mykey}; }

Writing documentation

=pod =head attribute Parameters : attribute value ( optional ) Description : attribute method can be used to read or update it ! Returns : current value Usage :         # use as reader         my $read = $object->attribute();         # use as writter         $object->attribute($new_value); =cut

        sub attribute {                 ... some code there ( or not ! )         }

... is difficult

rather than

Please use $object->{level1}{level2} to access the expected value ! # at this time, and it could never change ?

What is an object ?

•functions

•data

Small piece of code

Why object / module ?•code is organized

•Object is DRY, Lazy & Fun

•code is easier to read / share

•design & development can be dissociate

•easier to test

•provides fault containment

•reduce code maintenance

•simplify product evolution

•documentation is easier

Code organization

Object |__ Human | |___ Male | |___ Female | |__ Transport |___ Car |___ Plane |___ Bicycle

#!perl package Object::Transport::Car;

DRY : Do not Repeat Yourself

 package Document::Page;   sub create {       my $self = shift;       ...         do_some_stuff; # inner();       ...   }

  package Document::Special;   extends 'Document::Page';   augment 'create' => sub {       my $self = shift;       $self->do_some_extra_stuff;   };

•inheritance help you factorize your code•roles ( ~ interface )

Lazy

package Person; use Moose; # or any other object module

has 'age'    => ( is => 'rw', isa => 'Int', default => 42 );   package main; my $p = Person->new() or Person->new(age => 64); $current_age = $p->age(); $p->age($new_age);

•provide "new" method•provide accessors•parameters validation :

types, optional, default value...

Type checking

 package Person;   use Try::Tiny;   use Moose;   use Moose::Util::TypeConstraints;

  subtype 'Sex'       => as 'Str'       => where { $_ =~ m{^[mf]$}s };

  has 'sex'    => ( is => 'ro', isa => 'Sex', required =>  1 );   has 'age'    => ( is => 'rw', isa => 'Int', default  => 42 );  

  my $person = Person->new( sex => 'm', age => 45 );

  try {         Person->new( sex => 'unknown' );   } catch {          warn "Error has been detected";   };

Coercion

package My::Types::Date; use Moose::Util::TypeConstraints; use DateTime; # ...

subtype 'MyType:Day' => as 'DateTime'; coerce  'MyType:Day' => from 'Str' => via {     /^\d{4}\d{2}\d{2}$/ or croak "Unable to coerce '$_' into a valid date";     return DateTime::Format::DateParse->parse_datetime($_); };

package Test::Coerce; use Moose; use My::Types::Date;

has day => ( is  => 'rw', isa => 'MyType:Day',         coerce => 1);

package main; my $d1 = Test::Coerce->new(day => DateTime->now()); my $d2 = Test::Coerce->new(day => '20111130'); isa_ok $d2->day, 'DateTime', 'day is coerced to a DateTime object';

Which object library to choose ?•Moose

•Mouse

•Moo

•Mo

•M

•...

•fields

•Object::Tiny(::XS)

•Class::XSAccessor

•write your own simple Object ?and sometimes none

•the most advanced

•large ecosystem of extensions : MooseX : validate, getopt, singleton, types...

•can use advanced types and coercion methods

•hooks : before, after, inner, augment...

•startup time

•memory usage

•dependencies...

Moose : the fat one !

fat but so good...

Advantages :

Disadvantages :

•same goal as Mouse : provide a Moose lighter

•but provide "as little as possible" : minimalist

Moo : the light one !

package Demo::Moo; use Moo; with 'Some::Role'; has bar => (         is => 'rw',         isa => sub { $_[0] =~ /^[+-]?\d+$/ },         coerce => quote_sub q{ $_[0] + 1 unless $_[0] % 2 },         lazy => 1, );

•can use only one role at a time

•not available : super, override, inner, augment

•no initializer

Start with Moo and if you need more, switch to Mouse / Moose

•use Class::XSAccessor

•fast, easy but extremely limited

•only implement getters

•do not provide setters

Object::Tiny[::XS] : The fast one !

package MyClass; use Object::Tiny::XS qw{ list of attributes }; 1;   package main; my $object = MyClass->new( list => [ 42, 51 ], of => 'numbers' ); say join(',', @{$object->list}, $object->of); eval { $object->attributes( 63 ); } or warn "Update value failed !";

Writing your own object module ?package My::Object; # Inspired from Object::Tiny

sub import {     return unless shift eq __PACKAGE__;     my $pkg = caller;     @{"${pkg}::ISA"} = 'My::Object';     map {         my $method = "${pkg}::$_";         my $code = _get_accessor_for($_);         { no strict 'refs'; *$method = $code; }     } @_;     return 1; }

sub _get_accessor_for {     my ($key) = @_;     return unless caller eq __PACKAGE__; # Did I say private ?            defined $key and !ref $key and $key =~ /^[^\W\d]\w*\z/s

or croak("Invalid key '$key'");     sub {         my ($self, $v) = @_;         $self->{$key} = $v if defined $v;         $self->{$key};     }; }

sub new {     my $class = shift;     bless { @_ }, $class; } # that’s all !

Using your own object module

package main;

# initialize your object using new method my $elt  = MyClass->new(list => [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]);

# getter available my $list = $elt->list;

# setter available $elt->of('numbers');

package MyClass; use My::Object qw{ list of attributes };

# that's all you need !

create a class

start playing with it

Extreme performance with Class::XSAccessor

package SuperFast; use Class::XSAccessor     replace     => 1,     constructor => 'new',     accessors => {         speed => 'speed',     },     predicates => {         has_speed => 'speed', };

package main; my $o = SuperFast->new(speed => 'flash');

is($o->speed, 'flash'); $o->speed('max') and say $o->speed; is($o->speed, 'max');

•Fast read & write

•provide accessors, getters, setters and predicates

•use Devel::PPPort Perl Pollution Portability

Still want to keep using [better] hash ?

package Animal::Sounds; use fields qw(cat dog bird);

sub new {     my Animal::Sounds $self = shift;     $self = fields::new($self) unless ref $self;     return $self; }

package main; my Animal::Sounds $sound = Animal::Sounds->new(); # typed lexical $sound->{cat}   = 'miaaooo'; # ok $sound->{horse} = 'hiia'; # generates compile-time error   # No such class field "horse" in variable $sound of type Animal::Sounds

fields builtin provide compile-time class

Objects ( typed ) with named fields are as compact and as fast arrays to access

fields limit

# extract from field.pm *new = sub {         my $class = shift;         $class = ref $class if ref $class;         require Hash::Util;         my $self = bless {}, $class;                                                 # The lock_keys() prototype won't work since we require Hash::Util :(         &Hash::Util::lock_keys(\%$self, _accessible_keys($class));     # spent  6.00s making 20172 calls to Hash::Util::lock_keys, avg 297us/call     # spent 710ms making 20172 calls to fields::_accessible_keys, avg 35us/call         return $self; }

be careful in production ( perl >= 5.009 )

Trick : mock *fields::new depending on current env or switch to another object module

121     13.3ms 122     7.62ms 123     20.9ms 124     35.2ms 125     126     127     6.71s

128     61.6ms 129

•provides fast read access to attributes

•slow on object creation

Control hash life cycle

package main; use Human; use Test::More;

my $john = Human->new;

$john->{age} = 45; is($john->{age}, 45, "nothing special");

$john->{age} = 123; is($john->{age}, 99, "what happens there ?");

Still want to use hash ?

How is it possible ?

Use tie to hook

package Human;

sub TIESCALAR { die unless(ref $_[1] eq 'CODE'); my $cod=$_[1]; bless $cod, $_[0]; } sub STORE { my $self = shift; $self->(@_); } sub FETCH { shift->(); }

# many more available : DELETE, CLEAR, EXISTS, NEXTKEY...

sub new {     my $class = shift;     my $self = bless {}, $class;

    my $age;     tie $self->{age}, __PACKAGE__, sub {         my ($value) = @_;         return $age unless defined $value;         warn "You looks too old" and $value = 99 if $value >= 100;         $age = $value;         return $age;     };     $self; }

You can also tie array, hash...

When we should not choose object ?

"Premature optimization is the root of all evil" Tony Hoare

•performance ?

•difficult to learn ?

•need to create too many objects ?

•do not like abstraction ?

Performance problems ?

1/ Analyze the problem origin

•waiting for database

•waiting for IO

•CPU usage

•Memory usage

•.... use Devel::NYTProf

2/ then apply the correct solution•use parallel algorithm / poe

•use inline C / XS

• do not use object

•...

Object Benchmark

Moo : best average solution ( also consider Mouse ) Class::XSAccessor : performance fields : when many update and few object creation needed

Common Errors & Tricks

•make Moose package immutable no Moose; __PACKAGE__->meta->make_immutable();•object creation is time consuming : use cache or singleton if possible

•use factory for abstraction•fields use Hash::Util::Lock # bad idea if too many objects creation

•be careful with Time syscall, try to share it : my $now = Datetime->now();

•be careful with coercion # do not try to do too much ~ /black mag/ic