0
(Parameterized)
     Roles
    Shawn M Moore
 Best Practical Solutions

    http://sartak.org
"The more I use roles, the less I
 understand why anyone would
  want to use the inheritance
        model." - Ovid
"A rol...
Example Role
package Counter;
use Moose::Role;

has counter => (
   is   => 'ro',
   isa   => 'Int',
   default => 0,
);

...
Example Role
package Counter;
use Moose::Role;

has counter => (
   is   => 'ro',
   isa   => 'Int',
   default => 0,
);

...
Example Role
use MooseX::Declare;

role Counter {
   has counter => (
      is   => 'ro',
      isa   => 'Int',
      defa...
Consuming a Role

class Odometer with Counter {
   method reset(Crook $you) {
     $you->break_into($self);
     $self->co...
Odometer Fraud


http://en.wikipedia.org/wiki/Odometer_fraud
Consuming a Role

class Odometer with Counter {
   method reset(Crook $you) {
     $you->break_into($self);
      $self->c...
Consuming a Role

class Odometer extends Widget with Counter {
   method reset(Crook $you) {
     $you->break_into($self);...
class Odometer extends Widget {
   has counter => (
      is   => 'ro',
      isa   => 'Int',
      default => 0,
   );

 ...
Class Building
              Blocks
class Action::Throw
  with Action::Role::Direction
  with Action::Role::Item {
   …
}
...
Class Building
              Blocks
class Action::Throw
  with Action::Role::Direction
  with Action::Role::Item {
   …
}
...
Class Building
             Blocks
class Action::Throw
  extends Action::Direction
  extends Action::Item {
   …
}

class ...
Multiple Inheritance
class Action::Monster {
   has monster => (…);
   method name {
     $self->monster->name
   }
}
Multiple Inheritance

class Action::Direction {
   has direction => (…);
}




                     direction
Multiple Inheritance

class Action::Melee
extends Action::Direction
extends Action::Monster {
   …
}




                 ...
Multiple Inheritance
class Action::Direction {
   has direction => (…);
   method name {
     $self->direction->name
   }
...
Multiple Inheritance


 $melee->name


     name
Multiple Roles

class Action::Melee
with Action::Direction
with Action::Monster {
  …
}
Multiple Roles

Due to a method name conflict in roles
'Action::Direction' and 'Action::Monster', the
method 'name' must be...
AT
COMPILE
 TIME!
     !
Conflict Resolution

class Action::Melee
with Action::Direction
with Action::Monster {
   …
}
Conflict Resolution

class Action::Melee
with Action::Direction
with Action::Monster {
   method name { "melee" }
}
Conflict Resolution

class Action::Melee
with Action::Direction
with Action::Monster excludes name
{…}
class Action::Melee
with Action::Direction
alias { name => 'direction_name' }
with Action::Monster
alias { name => 'monste...
Conflict Resolution

Due to a method name conflict in roles
'Action::Direction' and 'Action::Monster', the
method 'name' mus...
Conflict Resolution

Due to a method name conflict in roles
'Action::Direction' and 'Action::Monster', the
method 'name' mus...
Conflict Resolution

Due to a method name conflict in roles
'Action::Direction' and 'Action::Monster', the
method 'name' mus...
Conflict Resolution

class Action::Melee
extends Action::Direction
extends Action::Monster
   excludes name
{…}
"Roles are the
 sound of diamond
   inheritance that
     people have
  stopped banging
their heads against."
        - hdp
Required Methods

role Action::Role::Monster {
   method monster {
     $self->tile->monster
   }

    method has_monster ...
Required Methods


class Action::Chat
with Action::Role::Monster {
   method run { print "woof" }
}
Required Methods



$chat->has_monster
Required Methods



   Can't locate object method "tile" via package
   "Action::Chat"




tile
Required Methods
role Action::Role::Monster {
   requires 'tile';

    method monster {
      $self->tile->monster
    }

...
Required Methods



'Action::Role::Monster' requires the method
'tile' to be implemented by 'Action::Chat'
Default
      Implementation
role Scan {
   requires 'entries';

    method ages {
      map { $_->age }
        $self->en...
Default
     Implementation

class Backend::Hash with Scan {
   method entries {
     values %{ $self->storage }
   }
}
Default
        Implementation
class Backend::DBI with Scan {
   method entries {
     $self->q("SELECT * …");
   }

    m...
Default
        Implementation
class Backend::DBI with Scan {
   method entries {
     $self->q("SELECT * …");
   }

    m...
Default
        Implementation
class Backend::DBI with Scan {
   method entries {
     $self->q("SELECT * …");
   }

    m...
"The more I use
  roles, the less I
  understand why
anyone would want
     to use the
inheritance model."
       - Ovid
Allomorphism


 Role = Type
Allomorphism


Role = Semantics
Allomorphism

Role = Type

Role = Semantics
Allomorphism


role Interface::Nonblocking {
   requires 'read', 'write';
}



                       read     write
Allomorphism


class Nonblocking::Socket
with Interface::Nonblocking {
   method read { … }
   method write { … }
}
Allomorphism


class Blocking::Socket {
   method read { … }
   method write { … }
}
Allomorphism

my $s = shift;

$s->does('Interface::Nonblocking')
  or confess 'No blocking!';

$s->write('          ');
Duck Typing

my $s = shift;

$s->can('read') && $s->can('write')
  or confess 'Need an interface';

$s->write('          '...
Allomorphism

my $s = shift;

$s->does('Interface::Nonblocking')
  or confess 'No blocking!';

$s->write('          ');

 ...
Allomorphism


has connection => (
  isa => 'Nonblocking::Socket',
);
Allomorphism


has connection => (
  isa => 'Nonblocking::Socket',
  does => 'Interface::Nonblocking',
);
"A role-aware type
 system allows you
to express yourself
     with better
    genericity."
    - chromatic
Extensions

class WWW::Mechanize::TreeBuilder
extends WWW::Mechanize {
   has tree => (
      …
   );
   around _make_requ...
Extensions

class Test::WWW::Mech::TreeBuilder
extends Test::WWW::Mechanize {
   has tree => (
      …
   );
   around _ma...
Extensions

class WWW::Mech::Cache::TreeBuilder
extends WWW::Mechanize::Cached {
   has tree => (
      …
   );
   around ...
Extensions



BAD!
Extensions

role WWW::Mechanize::TreeBuilder {
   has tree => (
      …
   );
   around _make_request {
      …
   }
}
Extensions


class Test::WWW::Mech::TreeBuilder
extends Test::WWW::Mechanize
with WWW::Mechanize::TreeBuilder {}
Extensions


class WWW::Mech::Cache::TreeBuilder
extends WWW::Mechanize::Cached
with WWW::Mechanize::TreeBuilder {}
Extensions


Roles >
Inheritance
Role -> Object
role Rootey {
   method su { … }
}

$jrock = User->new("jrockway");
$shawn = User->new("sartak");

Rootey->...
Plugins

MooseX::Object::Pluggable

     MooseX::Traits
Classes are Nouns
     Message
       User
      Service
     Request
     Response
Methods are Verbs
    send_message
      add_user
  connect_to_service
   handle_request
   serve_response
Roles are Adjectives
      HasPlugins
    RequiresPlugins
      Throwable
      Reblessing
  ExtendsObject::User
Parameterized Roles
role Scan {
   requires 'entries';

    method ages {
      map { $_->age }
        $self->entries
   ...
Parameterized Roles


 MooseX::Role::Parameterized




                 Moose
Parameterized Roles
role Counter (Int :$default = 0) {
   has counter => (
      is   => 'ro',
      isa   => 'Int',
     ...
Parameterized Roles


class Odometer
  with Counter(default => 10000) {
   …
}




                             66
Parameterized Roles

role Speedometer (Str :$method) {
   around $method {
      my $start = time;
      $orig->(@_);
    ...
Parameterized Roles
role WWW::Search (Str :$engine) {
   method search (Str $query) {
     if ($engine eq "Google") {
    ...
Parameterized Roles
package Valid::Adding;
use MooseX::Role::Parameterized;

role {
   $consumer->does('Adding') or die;
 ...
Roles
Class building blocks
Conflicts, resolution
 Required methods
Allomorphism (types)
Extensions, Plugins
    "adjective...
Thanks to my
  Vlad Dogsby
  Thomas Figg
Alexander Hoffer
  Stevan Little
  Lucas Oman
 Simon Pollard
Thanks to my



Kenichi Ishigaki
   charsbar++
See Also
           Moose::Manual::Roles


          "The Why of Perl Roles"


"Eliminating Inheritance via Smalltalk-Styl...
Upcoming SlideShare
Loading in...5
×

(Parameterized) Roles

1,870

Published on

Roles are an excellent object-oriented tool both for allomorphism and for
reuse.

Roles facilitate allomorphism by favoring "does this object do X" versus "is
this object a subclass of X". You often care more about capability than
inheritance. In a sense, roles encode types better than inheritance.

Roles also provide an excellent faculty for reuse. This effectively eliminates
multiple inheritance, which is often the only solution for sharing code between
unrelated classes.

Roles can combine with conflict detection. This eliminates accidental shadowing
of methods that is painful with multiple inheritance and mixins.

Parameterized roles (via MooseX::Role::Parameterized) improve the reusability
of roles by letting each consumer cater the role to its needs. This does
sacrifice some allomorphism, but there are ways to restore it.

Published in: Technology, Business
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,870
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
20
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • Presented YAPC::Asia, 2009-09-11.
    Tokyo Institute of Technology, Tokyo, Japan.
  • You've probably heard a lot about this new feature called "roles" lately. Not only are a lot of people talking about roles, but a lot of people are using roles. And for good reason!
  • Before I get into what a role is, and why roles are awesome, I want to just dive into what a role looks like.
  • You might notice a strong resemblance to regular Moose code. The only difference is that we "use Moose::Role" instead of "use Moose". This means that this package represents a role instead of a class. Oh, damnit, wait a second.
  • Much better. Anyway, so we have a role named Counter with an attribute and an increment method. Roles are not classes, so we can't call Counter->new. A role is more just a container for methods and attributes.
  • This is what consuming a role looks like. Here we have a class that needs a counter for keeping track of how many miles you have driven. "with" is the key word for consuming a role, much like "extends" is the key word for subclassing.
  • It is a very serious crime.
  • As you can see here, the role has added the counter attribute to Odometer. It also gained the increment method, which is presumably called by other parts of the system.
  • I must stress that consuming a role is not inheritance. You can still use inheritance alongside roles. What roles do are "flatten" its bits into the class.
  • The flattening is a lot like copying and pasting the code from the role into its consumer. That's even a pretty good description of what happens behind the scenes, though it doesn't happen at the strings-of-code level.
  • My favorite feature of roles is their reusability. One way to look at roles is that they are class building blocks.
  • If we factor out common behavior, we get to name that chunk of behavior, and reuse all that code. We know that these two actions have something to do with direction.
  • You might be wondering how any of this is better than multiple inheritance. Surely you know multiple inheritance is evil, right? RIGHT?

    By the way, did anyone here even know how to use multiple inheritance with MooseX::Declare? I sure didn't until I wrote this slide.
  • Let's look at an example of why multiple inheritance is maligned. Here we have a class with a "name" method.
  • Now we have another class. It just has a direction.
  • And now we have a class that inherits from both of those.
    So everything is fine right now. But at some point the requirements change.
  • We need a "name" method in Action::Direction for some other class.
  • This used to return monster name. Now this has changed without warning to the direction name, because Action::Direction is the leftmost parent. I hope your tests are very thorough!

    This is a huge pain to debug. Code reuse and cleanliness is often not worth this pain, so we avoid multiple inheritance.
  • If these were roles instead, something very different happens.
  • Once we add the "name" method to Direction, we get this error. I'll talk about what it means soon, but one nice thing about this error is Moose throws it..
  • ...immediately. You don't have to wait for the class to be instantiated, or the "name" method to be called. Moose throws conflict error at "with" time.
  • So this code is an error. But as alluded to in the error message, we can resolve this conflict. There are several ways to do this.
  • The class could define its own method. When a class defines a method and a role it consumes defines the same method, the class wins. So this resolves the conflict by overriding the "name" methods from the two roles. It's kind of like plugging your ears and yelling that everything is okay. But sometimes that really is all you need.
  • Another option is to exclude one of the conflicting methods. This way, Direction's "name" method is the one that is added to Melee. Obviously you can only use this where it makes sense. It probably doesn't make sense to use it in this example.

    XXX: This syntax doesn't actually work yet as of 2009-08-22. A failing test has been submitted!
  • Another option is to combine the two methods. Here we disambiguate the two conflicting "name" methods, then use them in our own "name" method which serves both roles well enough.

    I've found that this is usually the best solution.
  • So now we know what this error message means.
  • You can override the conflicting methods in the class, with or without reusing each conflicting method.
  • Or, if it makes sense, just exclude one of the methods so that it's no longer a conflict.
  • Try that with multiple inheritance. Yeah right!
  • Because of the flattening property of roles, and because of this conflict detection and resolution, the diamond inheritance problem doesn't apply to roles. Any ambiguity is a compile-time error, and the programmer has several tools to resolve such ambiguities. Role composition is much more pleasant to work with than multiple inheritance.
  • Suppose we have a role that assumes a particular method exists in each of its consumers. In this case, we're calling "tile" even though we don't know for sure that each consumer will have that method.
  • This code will work fine. We consume the role, Action::Chat gets the new methods, and everything is hunky-dory.
  • But at runtime when we try to call this method, we get an error.
  • This sucks. The author of the Monster role demands that consumers have a "tile" method. Perhaps the role's documentation does notify you that consumers must have it, but who even reads documentation any more? It would be nice to be able to codify this requirement.
  • And, of course, you can. If a role calls methods that it doesn't provide itself, the role should require them.
  • And like a conflict, we get this error at compile time. Much better than a method-missing error at runtime when we try to call ->tile. The Moose team really likes the fail-fast principle.
  • Here we have a role that requires an "entries" method, then builds on top of it with another method "ages".
  • We have an example backend that stores its entries in a hash table. We provide the "entries" method that the Scan role requires. That role gives us an "ages" method that calls "entries". Pretty straightforward use of roles.
  • When I was describing conflict resolution I mentioned how a method defined by the class wins over a method pulled in from a role. That's useful even outside of conflict resolution.
  • The ages method we define is doing a lot less work than the default implementation provided by the role would do. We don't need to pull in every field of every entry then pick out the values for age.

    However, the role's implementation is a good default that would be useful for a lot of these backends. Method overrides permit reuse but allow optimizations or alternate implementations where needed.
  • You might be asking why we bother to consume Scan even though we don't actually pull in any methods or attributes from it. That's next!
  • Roles have so many excellent features that I am starting to agree with this (admittedly radical) viewpoint myself.
  • Allomorphism is a fancy word that means a few things. For one, roles can be part of the type system. It's a lot like duck typing, but more explicit.
  • Allomorphism also means that a role implies semantics. Basically, every method implemented or required by the role must implement some specified behavior.
  • These imply similar consequences, so I'm going to explain them together.
  • Here we have a role that requires "read" and "write" methods from each of its consumers. Given the name of the role, we can guess that the role requires these methods to be nonblocking.
  • Here we define a class that does the nonblocking interface. Nonblocking::Socket promises that its read and write methods fulfill the socket's nonblocking requirements.
  • Here's another class, one whose read and write methods do block. Even though it fulfills the method name requirements of Interface::Nonblocking, this class would be lying if it declared that it does the role.
  • We can ask an object if it does a particular role. This will die if we try to use a Blocking::Socket here.

    This is better than checking "isa" because we actually care about capability. We don't care what $s's class is, or what its ancestors are. Any class can declare that it does the Interface::Nonblocking role, as long as it fulfills the role's contract.
  • Duck typing fulfills this same need. We also don't care about what class $s is, as long as it has the methods we want.

    However, the problem with duck typing is that merely having a set of methods does not imply semantics. Perhaps read and write are actually going to do text-to-speech and printing a term paper. Or worse, they might block.
  • In any case, because the Interface::Nonblocking role requires the write method, we know that $s will not only have it, but we know it will not block.
  • Moose has support for allomorphism in attributes. Instead of demanding that connection be a particular class...
  • … we can demand that the value of connection does a particular role.
  • chromatic's point here is that you can stop caring about hierarchy and start caring about capabilities. Allomorphism means you don't need to subclass someone's crack-fueled module. You need only fulfill its crack-fueled interface. It's OOP freedom.
  • Here's a quick tip for you. Say you're extending a module. Ordinarily you'd write a subclass, right?
  • You write good tests, so you subclass its test subclass too.
  • You also have to subclass all the other subclasses you use. Maybe it'd be better to just monkeypatch WWW::Mechanize. Who'd know?
  • No! Don't do it! You'll screw it up for anyone else who happens to use that module in your codebase.
  • Just make your extension a role.
  • Now you can apply this role to the existing subclasses of the module. There's no dot-dot-dot in the braces here. This is a full class definition.
  • That's really it. You don't need any other code. All the extension code is in your role. Nice and clean.
  • There, I said it!

    If you can implement your extension as a role, do it. Use inheritance sparingly.
  • I don't want to spend much time on this, but you can apply a role to a particular object. It doesn't have to be a full-blown class. It also won't affect any other objects. Which is good, because I wouldn't trust Jon with root.
  • Roles are nice for plugins too. I covered this heavily in my API Design talk. The idea is each plugin is just a role. These two modules make roles-as-plugins very easy.

    http://sartak.org/talks/yapc-asia-2009/api-design/
  • To give you an idea of how to think about roles, here's a pretty simple metaphor that works for me. Classes are nouns.
  • Methods are verbs. They are simple behaviors. They do things.
  • Roles are adjectives. These are all good role names. If you think of some piece of behavior as an adjective, that's a good sign that it can be factored out as a role.
  • This is a very specific kind of parameterized role. Each consumer parameterizes the "ages" method by providing an "entries" method. But that's not really what I'm talking about when I say parameterized role. If you can do this, do it. If you need something more advanced...
  • Parameterized roles are my biggest contribution to Moose. I'm happy with how they came out.
  • Here we have our old Counter role, but now each consumer can declare what default it wants for the attribute. If they choose nothing, they get the default of 0.

    rafl++ added parameterized role support to MooseX::Declare recently, so my examples get to look much nicer.
  • Using a parameterized role is pretty much the same. You just pass in the named arguments to "with". This was an example of parameterizing an attribute, and is probably the most common use of p-roles.
  • Here we have a role that wraps any method you give it with a bit of profiling code. This is sort of the inverse of "requires". Instead of the role telling you what method you need, you tell the role what method it needs to instrument. Someone once praised this type of p-role usage as resembling macros.
  • One really nice thing about parameterized roles is that they improve code reuse. All the consumer has to do is inform the role of which search engine they want to use. The role takes care of the rest. This means each consumer writes less code, because the parameterized role can build up more structure.
  • Here's another use case that was raised recently. The parameterized role could perform additional validation on each consumer. This is like "requires" for method names, but stronger.

    This is what pre-MooseX::Declare parameterized roles look like, due to language constraints.
  • Roles are all of these things. I hope I've convinced you to use roles in your next project's design. Thank you!
  • Thanks to these people who have reviewed my slides and offered excellent advice.
  • Thank you to Ishigaki-san for translating my slides!
  • http://search.cpan.org/perldoc?Moose::Manual::Roles

    http://www.modernperlbooks.com/mt/2009/04/the-why-of-perl-roles.html

    http://use.perl.org/~Ovid/journal/39404

    http://scg.unibe.ch/archive/phd/schaerli-phd.pdf
  • Transcript of "(Parameterized) Roles"

    1. 1. (Parameterized) Roles Shawn M Moore Best Practical Solutions http://sartak.org
    2. 2. "The more I use roles, the less I understand why anyone would want to use the inheritance model." - Ovid "A role-aware type system allows you to express yourself with better genericity." - chromatic "Roles are the sound of diamond inheritance that people have stopped banging their heads against." - hdp ( )
    3. 3. Example Role package Counter; use Moose::Role; has counter => ( is => 'ro', isa => 'Int', default => 0, ); sub increment { my $self = shift; $self->counter($self->counter + 1); }
    4. 4. Example Role package Counter; use Moose::Role; has counter => ( is => 'ro', isa => 'Int', default => 0, ); sub increment { my $self = shift; $self->counter($self->counter + 1); } use Moose
    5. 5. Example Role use MooseX::Declare; role Counter { has counter => ( is => 'ro', isa => 'Int', default => 0, ); method increment { $self->counter($self->counter + 1); } }
    6. 6. Consuming a Role class Odometer with Counter { method reset(Crook $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } } with
    7. 7. Odometer Fraud http://en.wikipedia.org/wiki/Odometer_fraud
    8. 8. Consuming a Role class Odometer with Counter { method reset(Crook $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } } counter
    9. 9. Consuming a Role class Odometer extends Widget with Counter { method reset(Crook $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } }
    10. 10. class Odometer extends Widget { has counter => ( is => 'ro', isa => 'Int', default => 0, ); method increment { $self->counter($self->counter + 1); } method reset(Crook $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } }
    11. 11. Class Building Blocks class Action::Throw with Action::Role::Direction with Action::Role::Item { … } class Action::Melee with Action::Role::Monster with Action::Role::Direction { … }
    12. 12. Class Building Blocks class Action::Throw with Action::Role::Direction with Action::Role::Item { … } class Action::Melee with Action::Role::Monster with Action::Role::Direction { … }
    13. 13. Class Building Blocks class Action::Throw extends Action::Direction extends Action::Item { … } class Action::Melee extends Action::Monster extends Action::Direction { … }
    14. 14. Multiple Inheritance class Action::Monster { has monster => (…); method name { $self->monster->name } }
    15. 15. Multiple Inheritance class Action::Direction { has direction => (…); } direction
    16. 16. Multiple Inheritance class Action::Melee extends Action::Direction extends Action::Monster { … } …
    17. 17. Multiple Inheritance class Action::Direction { has direction => (…); method name { $self->direction->name } } name
    18. 18. Multiple Inheritance $melee->name name
    19. 19. Multiple Roles class Action::Melee with Action::Direction with Action::Monster { … }
    20. 20. Multiple Roles Due to a method name conflict in roles 'Action::Direction' and 'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee' Moose
    21. 21. AT COMPILE TIME! !
    22. 22. Conflict Resolution class Action::Melee with Action::Direction with Action::Monster { … }
    23. 23. Conflict Resolution class Action::Melee with Action::Direction with Action::Monster { method name { "melee" } }
    24. 24. Conflict Resolution class Action::Melee with Action::Direction with Action::Monster excludes name {…}
    25. 25. class Action::Melee with Action::Direction alias { name => 'direction_name' } with Action::Monster alias { name => 'monster_name' } { method name { loc '%1 (at %2)', $self->monster_name, $self->direction_name; } } 2
    26. 26. Conflict Resolution Due to a method name conflict in roles 'Action::Direction' and 'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee'
    27. 27. Conflict Resolution Due to a method name conflict in roles 'Action::Direction' and 'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee'
    28. 28. Conflict Resolution Due to a method name conflict in roles 'Action::Direction' and 'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee'
    29. 29. Conflict Resolution class Action::Melee extends Action::Direction extends Action::Monster excludes name {…}
    30. 30. "Roles are the sound of diamond inheritance that people have stopped banging their heads against." - hdp
    31. 31. Required Methods role Action::Role::Monster { method monster { $self->tile->monster } method has_monster { $self->tile->has_monster } }
    32. 32. Required Methods class Action::Chat with Action::Role::Monster { method run { print "woof" } }
    33. 33. Required Methods $chat->has_monster
    34. 34. Required Methods Can't locate object method "tile" via package "Action::Chat" tile
    35. 35. Required Methods role Action::Role::Monster { requires 'tile'; method monster { $self->tile->monster } method has_monster { $self->tile->has_monster } }
    36. 36. Required Methods 'Action::Role::Monster' requires the method 'tile' to be implemented by 'Action::Chat'
    37. 37. Default Implementation role Scan { requires 'entries'; method ages { map { $_->age } $self->entries } }
    38. 38. Default Implementation class Backend::Hash with Scan { method entries { values %{ $self->storage } } }
    39. 39. Default Implementation class Backend::DBI with Scan { method entries { $self->q("SELECT * …"); } method ages { $self->q("SELECT age …"); } }
    40. 40. Default Implementation class Backend::DBI with Scan { method entries { $self->q("SELECT * …"); } method ages { $self->q("SELECT age …"); } }
    41. 41. Default Implementation class Backend::DBI with Scan { method entries { $self->q("SELECT * …"); } method ages { $self->q("SELECT age …"); } } Scan
    42. 42. "The more I use roles, the less I understand why anyone would want to use the inheritance model." - Ovid
    43. 43. Allomorphism Role = Type
    44. 44. Allomorphism Role = Semantics
    45. 45. Allomorphism Role = Type Role = Semantics
    46. 46. Allomorphism role Interface::Nonblocking { requires 'read', 'write'; } read write
    47. 47. Allomorphism class Nonblocking::Socket with Interface::Nonblocking { method read { … } method write { … } }
    48. 48. Allomorphism class Blocking::Socket { method read { … } method write { … } }
    49. 49. Allomorphism my $s = shift; $s->does('Interface::Nonblocking') or confess 'No blocking!'; $s->write(' ');
    50. 50. Duck Typing my $s = shift; $s->can('read') && $s->can('write') or confess 'Need an interface'; $s->write(' ');
    51. 51. Allomorphism my $s = shift; $s->does('Interface::Nonblocking') or confess 'No blocking!'; $s->write(' '); write
    52. 52. Allomorphism has connection => ( isa => 'Nonblocking::Socket', );
    53. 53. Allomorphism has connection => ( isa => 'Nonblocking::Socket', does => 'Interface::Nonblocking', );
    54. 54. "A role-aware type system allows you to express yourself with better genericity." - chromatic
    55. 55. Extensions class WWW::Mechanize::TreeBuilder extends WWW::Mechanize { has tree => ( … ); around _make_request { … } }
    56. 56. Extensions class Test::WWW::Mech::TreeBuilder extends Test::WWW::Mechanize { has tree => ( … ); around _make_request { … } }
    57. 57. Extensions class WWW::Mech::Cache::TreeBuilder extends WWW::Mechanize::Cached { has tree => ( … ); around _make_request { … } } Mech
    58. 58. Extensions BAD!
    59. 59. Extensions role WWW::Mechanize::TreeBuilder { has tree => ( … ); around _make_request { … } }
    60. 60. Extensions class Test::WWW::Mech::TreeBuilder extends Test::WWW::Mechanize with WWW::Mechanize::TreeBuilder {}
    61. 61. Extensions class WWW::Mech::Cache::TreeBuilder extends WWW::Mechanize::Cached with WWW::Mechanize::TreeBuilder {}
    62. 62. Extensions Roles > Inheritance
    63. 63. Role -> Object role Rootey { method su { … } } $jrock = User->new("jrockway"); $shawn = User->new("sartak"); Rootey->meta->apply($shawn); $shawn->su; # ok $jrock->su; # dies!
    64. 64. Plugins MooseX::Object::Pluggable MooseX::Traits
    65. 65. Classes are Nouns Message User Service Request Response
    66. 66. Methods are Verbs send_message add_user connect_to_service handle_request serve_response
    67. 67. Roles are Adjectives HasPlugins RequiresPlugins Throwable Reblessing ExtendsObject::User
    68. 68. Parameterized Roles role Scan { requires 'entries'; method ages { map { $_->age } $self->entries } }
    69. 69. Parameterized Roles MooseX::Role::Parameterized Moose
    70. 70. Parameterized Roles role Counter (Int :$default = 0) { has counter => ( is => 'ro', isa => 'Int', default => $default, ); sub increment { my $self = shift; $self->counter($self->counter + 1); } }
    71. 71. Parameterized Roles class Odometer with Counter(default => 10000) { … } 66
    72. 72. Parameterized Roles role Speedometer (Str :$method) { around $method { my $start = time; $orig->(@_); print "$method " . (time - $start); } } 66
    73. 73. Parameterized Roles role WWW::Search (Str :$engine) { method search (Str $query) { if ($engine eq "Google") { REST::Google::Search->… } elsif ($engine eq "Yahoo!") { Yahoo::Search->… } elsif ($engine eq "Bing") { die "seriously?"; } # post-process @results } } 66
    74. 74. Parameterized Roles package Valid::Adding; use MooseX::Role::Parameterized; role { $consumer->does('Adding') or die; $consumer->add(2, 6) == 8 or die; method safe_add => sub { … }; }; 66
    75. 75. Roles Class building blocks Conflicts, resolution Required methods Allomorphism (types) Extensions, Plugins "adjectives"
    76. 76. Thanks to my Vlad Dogsby Thomas Figg Alexander Hoffer Stevan Little Lucas Oman Simon Pollard
    77. 77. Thanks to my Kenichi Ishigaki charsbar++
    78. 78. See Also Moose::Manual::Roles "The Why of Perl Roles" "Eliminating Inheritance via Smalltalk-Style Traits" "Traits - Composing Classes from Behavioral Building Blocks"
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×