Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Metaobject Protocol

Upasana
September 05, 2015

Introduction to Metaobject Protocol

Upasana

September 05, 2015
Tweet

More Decks by Upasana

Other Decks in Programming

Transcript

  1. Motivation behind this talk • To share what I learnt

    during my internship • Some problems can be solved in a better way
  2. How a class looks like? • Attributes – Is read

    only or read-write – Type (int, float etc.) – Default value if any – Getter – Setter
  3. Classes in Perl • Perl doesn't provide any special syntax

    for classes • Perl packages are classes
  4. Attributes in Perl classes • No special syntax or support

    for declaring and manipulating attributes • Attributes are stored in the object itself • As a hash of key-value pairs
  5. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  6. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  7. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  8. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  9. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  10. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  11. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  12. OOP in Perl package Rectangle; sub new { my $self

    = shift; my $attributes = {@_}; bless $attributes, $self; } 1; Rectangle->new( height => 10, width => 20, );
  13. What is MOP? • provides the vocabulary to access and

    manipulate the structure and behavior of objects.
  14. Functions of MOP • Creating and deleting new classes •

    Changing the class structure • Changing methods of the class
  15. History of MOP • First introduced in the Smalltalk •

    Common LISP Object System (CLOS) was influenced by Smalltalk • CLOS allowed multiple inheritance unlike Smalltalk
  16. MOP in modern languages • Javascript has Joose • OpenC++

    • Java has Reflection API • Perl has Moose
  17. Testing • I work at booking.com • Our website is

    moving very fast • Many rollouts in a day
  18. Testing • We don't really have test suites • People

    are reluctant to do rollouts • Everything needs to be tested manually
  19. Testing package Web::Handler { has 'search' => ( url =>

    '/search', #... ); has 'hotel' => ( url => '/hotel', #... ); # ... }
  20. Testing package Web::Handler { has 'search' => ( url =>

    '/search', #... ); has 'hotel' => ( url => '/hotel', #... ); # ... }
  21. Testing package Web::Handler { has 'search' => ( url =>

    '/search', #... ); has 'hotel' => ( url => '/hotel', #... ); # ... }
  22. Testing # don't expect this to compile my $attr =

    Web::Handler->meta->get_attributes_list; foreach my $a ( @$attr ) { next unless $a->attribute_exists('url'); my $url = $a->get_attribute('url'); LWP::Simple::get($url); }
  23. Testing # don't expect this to compile my $attr =

    Web::Handler->meta->get_attributes_list; foreach my $a ( @$attr ) { next unless $a->attribute_exists('url'); my $url = $a->get_attribute('url'); LWP::Simple::get($url); }
  24. Testing # don't expect this to compile my $attr =

    Web::Handler->meta->get_attributes_list; foreach my $a ( @$attr ) { next unless $a->attribute_exists('url'); my $url = $a->get_attribute('url'); LWP::Simple::get($url); }
  25. Testing # don't expect this to compile my $attr =

    Web::Handler->meta->get_attributes_list; foreach my $a ( @$attr ) { next unless $a->attribute_exists('url'); my $url = $a->get_attribute('url'); LWP::Simple::get($url); }
  26. Testing # don't expect this to compile my $attr =

    Web::Handler->meta->get_attributes_list; foreach my $a ( @$attr ) { next unless $a->attribute_exists('url'); my $url = $a->get_attribute('url'); LWP::Simple::get($url); }
  27. Testing # don't expect this to compile my $attr =

    Web::Handler->meta->get_attributes_list; foreach my $a ( @$attr ) { next unless $a->attribute_exists('url'); my $url = $a->get_attribute('url'); LWP::Simple::get($url); }
  28. ORM my $sql_parser = SQL::Parser->new( $create_table_statement ); my $class_name =

    $sql_parser->table_name; my $c = Moose::Meta::Class->create( $class_name );
  29. ORM my $sql_parser = SQL::Parser->new( $create_table_statement ); my $class_name =

    $sql_parser->table_name; my $c = Moose::Meta::Class->create( $class_name );
  30. ORM my $sql_parser = SQL::Parser->new( $create_table_statement ); my $class_name =

    $sql_parser->table_name; my $c = Moose::Meta::Class->create( $class_name );
  31. ORM my $sql_parser = SQL::Parser->new( $create_table_statement ); my $class_name =

    $sql_parser->table_name; my $c = Moose::Meta::Class->create( $class_name );
  32. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  33. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  34. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  35. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  36. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  37. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  38. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  39. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  40. ORM $c->set_superclass( 'SomeDB::Class::Thing' ); foreach my $f ( $sql_parser->fields )

    { $c->add_attribute( Moose::Meta::Attribute->new( $f->name, isa => find_type_constraint( $f->type ), reader => 'get_' . $f->name, writer => 'set_' . $f->name, ); ); }
  41. Creating a class at runtime • Perl class is a

    package • Every package has a symbol table
  42. Symbol table • Hash of subroutines/variables defined in a package

    • package name with two colons appended $Rectangle::
  43. Symbol table • Hash of subroutines/variables defined in a package

    • package name with two colons appended $Rectangle::
  44. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  45. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  46. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  47. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  48. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  49. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  50. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  51. package Metaclass; sub create_class { my ($self, %options) = @_;

    my $class = $options{ package }; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop } 1;
  52. Metaclass->create_class( package => 'Rectangle', methods => { new => sub

    { my ($self) = shift; my $attributes = {@_}; return bless $attributes, $self; }, } ); Rectangle->new( height => 10, Width => 20 );
  53. Metaclass->create_class( package => 'Rectangle', methods => { new => sub

    { my ($self) = shift; my $attributes = {@_}; return bless $attributes, $self; }, } ); Rectangle->new( height => 10, Width => 20 );
  54. Metaclass->create_class( package => 'Rectangle', methods => { new => sub

    { my ($self) = shift; my $attributes = {@_}; return bless $attributes, $self; }, } ); Rectangle->new( height => 10, Width => 20 );
  55. Metaclass->create_class( package => 'Rectangle', methods => { new => sub

    { my ($self) = shift; my $attributes = {@_}; return bless $attributes, $self; }, } ); Rectangle->new( height => 10, Width => 20 );
  56. Metaclass->create_class( package => 'Rectangle', methods => { new => sub

    { my ($self) = shift; my $attributes = {@_}; return bless $attributes, $self; }, } ); Rectangle->new( height => 10, Width => 20 );
  57. • There is not yet a way to get class

    of a class, i.e. metaclass
  58. sub create_class { my ($self, %options) = @_; my $class

    = $options{ package }; $options{ methods }->{ meta } = \&get_meta; my $methods = $options{ methods }; while( my ($method, $body) = each( %$methods ) ) { no strict 'refs'; *{ "${class}::$method" } = $body; } # end while loop store_metaclass( $class, \%options ); }
  59. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  60. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  61. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  62. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  63. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  64. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  65. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  66. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  67. sub create_class { my ($self, %options) = @_; my $class

    = $options{ package }; $options{ methods }->{ meta } = \&get_meta; my $methods = $options{ methods }; no strict 'refs'; while( my ($method, $body) = each( %$methods ) ) { *{ "${class}::$method" } = $body; } # end while loop use strict; store_metaclass( $class, \%options ); }
  68. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  69. sub get_meta { my $class = shift; Metaclass->get_metaclass( $class );

    }; my %meta_to_class; sub get_metaclass { my ($self) = shift; return $meta_to_class{ $_[ 0 ] }; } sub store_metaclass { $meta_to_class{ $_[ 0 ] } = $_[ 1 ]; }
  70. Introspection Metaclass->create_class( package => 'Rectangle', methods => { new =>

    sub { my ($self) = shift; my $attributes = {@_}; return bless $attributes, $self; }, }, ); print Dumper( Rectangle->meta );
  71. { 'package' => 'Rectangle', 'methods' => { 'meta' => sub

    { "DUMMY" }, 'new' => sub { "DUMMY" } } };
  72. { 'package' => 'Rectangle', 'methods' => { 'meta' => sub

    { "DUMMY" }, 'new' => sub { "DUMMY" } } };
  73. “Manipulating stashes (Perl's symbol tables) is occasionally necessary, but incredibly

    messy, and easy to get wrong. This module hides all of that behind a simple API.” `man Package::Stash`
  74. But why? • Metaclass.pm is very basic • But actually

    Metaclasses are not so simple • Look at Moose
  75. Inheritance • A has a method i-foo – Calls c-bar

    of MetaA • B inherits from A • B has i-foo • MetaB may not have c-bar
  76. Inheritance • MetaA has a method c-foo • c-foo needs

    to call i- bar in A • MetaB inherits from MetaA • B has to has i-bar
  77. Metaclass compatibility (Moose) • Does parent & child metaclasses have

    any common ancestors? – If yes, then \o/ – else, die • Moose::Exception::CannotFixMetaclassComp atibility
  78. Mixins • A class that contains a combination of methods

    from other classes • 'Included' rather than 'inherited' • Moose roles are similar to mixins
  79. Rules of mixins-based inheritance • Order of the mixins matter

    • Mixins take precedence over non-mixins
  80. Rules of mixins-based inheritance • Methods in M2 will take

    precedence over A • Methods in M1 will take precedence over M2
  81. Rules of mixins-based inheritance • Methods in B will take

    precedence over M1 • Methods in M3 will take precedence over B
  82. Introspection • For getting attributes: Rectangle->meta->get_attributes_list(); • For getting methods:

    Rectangle->meta->get_methods_list(); • For getting superclasses: Rectangle->meta->superclasses;
  83. Changing Class definition • For adding a new attribute: Rectangle->meta->add_attribute(...);

    • For adding a new method: Rectangle->meta->add_method(...);
  84. Drawbacks of MOP • Makes things slow • While using

    Moose, don't forget to do: __PACKAGE__->meta->make_immutable; – It tells Moose that you are – not going to change your – class at runtime
  85. Bibliography • The Art of the Metaobject Protocol • Metaclass

    Composition Using Mixin-Based Inheritance by Noury Bouraqadi • Wikipedia • Moose documentation • And lots of other random resources on the internet