Reflex - How Does It Work? (extended dance remix)
-
Upload
rocco-caputo -
Category
Technology
-
view
3.235 -
download
1
description
Transcript of Reflex - How Does It Work? (extended dance remix)
ReflexHow does it work?
Rocco Caputo – @rcaputoYAPC::NA
Tuesday, 28 June 2011Around Teatime
Feedback, plx.
This talk describes the
Github Version
Who Does He Think He Is?
• Rocco Caputo or “rcaputo” most places.
• http://search.cpan.org/~rcaputo/
• https://github.com/rcaputo
• http://twitter.com/rcaputo
“The POE Guy”
⃠“The POE Guy”⃠
Curtis “Ovid” Poe
Edgar Allan Poe
Anne Decatur
Danielewski
The Other Committers• Adam Kennedy
• Benjamin Smith
• Casey West
• Chris Fedde
• Chris Williams
• David Davis
• David Webb
• Hinrik Örn Sigurðsson
• jmadler
• Jonathan Steinert
• Larwan Berke
• Martijn van Beers
• Matt Cashner
• Matt Sickler
• Perl Whore
• Philip Gwyn
• Tom Feist
• Yuval Kogman
The CPAN AuthorsAlejandro Imass • Alessandro Ranellucci • Anatoly Sharifulin • Andrew A. Chen • Andrew Hoying • Andrew Sterling Hanenkamp • Andrew V. Purshottam • Andy Grundman • Artur Bergman • Benjamin Trott • Brendan Beveridge • Chris Cobb • Chris Prather • Christian-Rolf Gruen • Curtis Hawthorne • Daisuke Maki • Daisuke Murase • Damien Krotkine • Dan McCormick • David Golden • David Snopek • Denis Pokataev • Dmitry Karasik • dmitry kim • Eriam Schaffter • Eric Waters • Erick Calder • George Nistorica • Greg Fast • Guillermo Roditi • Hans Dieter Pearcey • Ivan B. Serezhkin • J. J. Merelo Guervos • Jan Henning Thorsen • Jason McManus • Jeff Bisbee • Jeff Goff • Jerome Quelin • Johannes Plunien • Jonathan Ringstad • Jozef Kutej • Justin Hunter • Kazuhiro Osawa • Kevin L. Esteb • Kirill Miazine • Larry Shatzer Jr • Loic TROCHET • Marc Lehmann • Marc Mims • Mark A. Hershberger • Mark McConnell • Mark Morgan • Markus Mueller • Matthew O’Connor • Michael Ching • Michael R. Davis • Michael Schilli • Mike Fletcher • Mike Schroeder • Mischa Spiegelmock • MOCK • MSERGEANT • Nicholas Perez • Olivier ‘dolmen’ Mengue • Paul David Tinsley • Paul Driver • Paul Evans • Paul G Webster • Paul Visscher • Pavel Boldin • Pedro Melo • Peter Guzis • Przemyslaw Iskra • Rafael Kitover • Richard Clamp • Rob Bloodgood • Rob Partington • Robert ‘phaylon’ Sedlacek • Sawyer X • Scott Beck • Scott McCoy • Sean Egan • Sebastien Aperghis-Tramoni • Sergey Kotenko • Sergey Skvortsov • Sjors Gielen • Stephen Adkins • Steve James • Steve McNabb • Takeshi Miki • Tatsuhiko Miyagawa • Thiago Berlitz Rondon • Tony Cook • Torsten Raudssus • [email protected] • William Travis Holton • Yuji Suzuki • Yves Blusseau • Zoffix Znet
EOVERFLOWThe Users
Okay, me too.
Digression:See Digression
ReflexWhat Is It?
Rocco Caputo – @rcaputoYAPC::NA
Tuesday, 28 June 2011Around Teatime
ReflexWhat Isn’t It?
Rocco Caputo – @rcaputoYAPC::NA
Tuesday, 28 June 2011Around Teatime
What can’t you do with a drunken sailor?
Reflex is an only child waiting in
the park.
⃠Reflex is an only child waiting in
the park.⃠
Reflex isn’t an event loop.
CPAN already has too many
event loops.
Event loops are the means, not the ends.
Reflex is eventy without so much loopy.
Nor is Reflex an object system.
CPAN already has too many
object systems.
Reflex uses Moose
But Why?!“I am disappointed that after all this time we have no consensus on how to say in Perl ‘Class X has attribute Y’ when so many other languages have solutions that have freed their users’ minds up to move on to higher-level problems.”— Peter Scott, on the Perl 5 Porters Mailing List
But Why?!
• Moose lets me solve higher-level problems.
• Moose has sufficient adoption to survive.
• The Meta-Object Protocol is insanely useful.
• Moose will get better as native Perl OO improves.
ReflexWhat Is It?
Rocco Caputo – @rcaputoYAPC::NA
Tuesday, 28 June 2011Around Teatime
The ORM of Event Loops!!
Marketing bullshit aside...
Reactive Program Building
Blocks
Building Blocks
Provided by Moose
Plus a Reactor provided by Your Favorite Event
Loop
Invisible Event Loop
Like ORMs hide SQL behind
objects...
ORM
DB
Reflex
The Events Can
Reflex hides event
loops behind
objects...
Reflex is Modern
POE
Reflex is Modern POE
• POE = Perl Object Environment
• Modern Perl objects are very different than 1998 Perl objects.
• POE has a lot of users.
• Start over with Reflex so POE remains compatible.
Reflex unifies eventy
interaction.
Rule 34 for Perl
If you can think of it, CPAN has a module for it.
TIMTOWTDI
If you can think of it, CPAN has 2+ incompatible modules for it...
TIMTOWTDI
... and someday you’ll need to
use both at once.
TIMTOWTDI
• There’s more than one way to pass events around.
• The “best” way... depends.
• Choosing poorly limits later options.
• Reflex supports any or all at once.
3 ½ Rules Make it Work
⁓ 1 ⁓Objects must not dictate callback mechanisms.
⁓ 1.5 ⁓Users define
how they receive callbacks.
⁓ 2 ⁓The base system must support all desired callback
types.
⁓ 3 ⁓Reflex must do
just enough work to be correct.
Reflex unifies eventy program
composition.
“How Eventy Programs are Put Together”
Static Composition
• Code is bolted together before running.
• Subclassing.
• Role composition.
• All pieces built and destroyed together.
• Most eventy abstractions ignore this.
Dynamic Composition
• Event watchers are created,
• related (has-a),
• communicated (with),
• and destructicated
• at run time.
• Lather, rinse, repeat until the owner ends.
Dynamic Lifetimes• Object provides service to one owner.
• Object life is briefer than its owner.
• Object’s lifespan matches its owner.
• Good candidate for static composition.
• Object provides service to 2+ subscribers.
• Good candidate for breadboarding or publish/subscribe.
Reflex Embraces,
Extends and Consumes Both
Examples are next.
ReflexHow to Use It!
Rocco Caputo – @rcaputoYAPC::NA
Tuesday, 28 June 2011Around Teatime
Anonymous Callbacks
Ye Olde Coderefuse Reflex::Interval;
• my $i_one = Reflex::Interval->new(• interval => 1,
on_tick => sub { print "tick one...\n" },);
my $i_two = Reflex::Interval->new( interval => 0.5, on_tick => sub { print "tick two...\n" },);
Reflex->run_all();
Ye Olde Coderefuse Reflex::Interval;
my $i_one = Reflex::Interval->new( interval => 1,
• on_tick => sub { print "tick one...\n" },);
my $i_two = Reflex::Interval->new( interval => 0.5, on_tick => sub { print "tick two...\n" },);
Reflex->run_all();
Ye Olde Coderefuse Reflex::Interval;
my $i_one = Reflex::Interval->new( interval => 1, on_tick => sub { print "tick one...\n" },);
• my $i_two = Reflex::Interval->new(• interval => 0.5,
on_tick => sub { print "tick two...\n" },);
Reflex->run_all();
Ye Olde Coderefuse Reflex::Interval;
my $i_one = Reflex::Interval->new( interval => 1, on_tick => sub { print "tick one...\n" },);
my $i_two = Reflex::Interval->new( interval => 0.5,
• on_tick => sub { print "TICK TWO!!!\n" },);
Reflex->run_all();
tick one...TICK TWO!!!TICK TWO!!!tick one...TICK TWO!!!TICK TWO!!!tick one...TICK TWO!!!TICK TWO!!!^C
Ye Olde Coderef
Ye Olde Coderef - The Good
• Quick and dead simple to use.
• Convenient and fast for small things.
• Parsimonious use of memory.
Ye Olde Coderef - The Bad
• Circular references and memory leaks—unless you explicitly manage memory.
• Anti-pattern if done solely for speed...
• Implementation detail wags the dog.
• Giving up OO benefits for speed.
Ye Olde Coderef - The Ugly# A twisty maze of coderefs, all alike.
Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub { Watcher( sub {
Method Callbacks
Method Callbacks{
• package TickingThing;• use Moose; extends "Reflex::Base";
use Reflex::Interval;
has ticker => ( isa => "Reflex::Interval", is => "rw", default => sub { my $self = shift(); Reflex::Interval->new( interval => 1, on_tick => [ $self, "callback" ], ) }, );
sub callback { print "method got tick...\n" }}
Thing->new()->run_all();
Method Callbacks{ package TickingThing; use Moose; extends "Reflex::Base"; use Reflex::Interval;
• has ticker => (• isa => "Reflex::Interval", is => "rw",
default => sub { my $self = shift(); Reflex::Interval->new( interval => 1, on_tick => [ $self, "callback" ], ) }, );
sub callback { print "method got tick...\n" }}
Thing->new()->run_all();
{ package TickingThing; use Moose; extends "Reflex::Base"; use Reflex::Interval;
has ticker => ( isa => "Reflex::Interval", is => "rw", default => sub { my $self = shift(); Reflex::Interval->new(
• interval => 1, on_tick => [ $self, "callback" ], ) }, );
sub callback { print "method got tick...\n" }}
Thing->new()->run_all();
Method Callbacks
{ package TickingThing; use Moose; extends "Reflex::Base"; use Reflex::Interval;
has ticker => ( isa => "Reflex::Interval", is => "rw", default => sub { my $self = shift(); Reflex::Interval->new(
• interval => 1, on_tick => [ $self, "callback" ], ) }, );
• sub callback { print "method got tick...\n" }}
Thing->new()->run_all();
Method Callbacks
method got tick...method got tick...method got tick...method got tick...method got tick...method got tick...method got tick...method got tick...method got tick...method got tick...^C
Method Callbacks
Methods – The Costs
• Verbose syntax.
• Perl OO is slower than code references and closures.
• Perl OO requires memory.
• Moose uses memory, too.
Methods – The Benefits
• Syntax gets sweeter.
• Object oriented design is cleaner and more extensible.
• No twisty maze of nested code references, all different.
• It’s all “standard” Perl and/or Moose.
Methods – The Future
• Avoiding flexibility by design is a dead end.
• Speed and size improve over time.
• Perl may get its own MOP.
• Moose and Moose-alikes may converge as Perl standardizes common features.
“But callbacks suck!” you say?
Promises
“an object that acts as a proxy for a result that is initially not known, usually because the computation of its value has not yet completed.”
— Wikipedia
What’s a Promise?
What’s a Promise?
Blah, blah, blah.
Promises• Asynchronous event generator.
• Create it.
• Do other stuff while it’s working.
• Pick up the next result later.
• Blocks or returns “incomplete” if not done.
• Implementation decides which.
• Future release may let the caller decide.
use Reflex::Interval;
• my $one = Reflex::Interval->new(• interval => 1• );
my $two = Reflex::Interval->new( interval => 2);
Timer Promise (1 of 2)
use Reflex::Interval;
my $one = Reflex::Interval->new( interval => 1);
• my $two = Reflex::Interval->new(• interval => 2• );
Timer Promise (1 of 2)
print "Before : ", time(), "\n";
• my $event = $two->next();• print "After two: ", time(), "\n";
$event = $one->next();print "After one: ", time(), "\n";
Timer Promise (2 of 2)
print "Before : ", time(), "\n";
my $event = $two->next();print "After two: ", time(), "\n";
• $event = $one->next();• print "After one: ", time(), "\n";
Timer Promise (2 of 2)
% perl promises.pl • Before : 1295045065• After two: 1295045067
After one: 1295045067
Eventy Timer Promise
Blocked 2 seconds.
% perl promises.pl Before : 1295045065
• After two: 1295045067• After one: 1295045067
Eventy Timer Promise
Instant result.
When to Avoid! Shun!
• Only use Reflex’s promises for asynchronous tasks that may need to wait.
• Synchronous generators and iterators are more efficient for computation.
“We were somewhere around Asheville in the
heart of the Blue Ridge Mountains when the Moose began to
take hold.”
Subclassing
Simpler Method Callbacks
Method Callbacks{ package TickingThing; use Moose; extends "Reflex::Base"; use Reflex::Interval;
• has ticker => (• isa => "Reflex::Interval", is => "rw",
default => sub { my $self = shift(); Reflex::Interval->new( interval => 1, on_tick => [ $self, "callback" ], ) }, );
sub callback { print "method got tick...\n" }}
Thing->new()->run_all();
Method Callbacks{ package TickingThing; use Moose; extends "Reflex::Base"; use Reflex::Interval;
has ticker => ( isa => "Reflex::Interval", is => "rw", default => sub { my $self = shift(); Reflex::Interval->new(
• interval => 1, on_tick => [ $self, "callback" ], ) }, );
• sub callback { print "method got tick...\n" }}
Thing->new()->run_all();
Method Callbacks{ package TickingThing; use Moose; extends "Reflex::Base"; use Reflex::Interval;
has ticker => ( isa => "Reflex::Interval", is => "rw", default => sub { my $self = shift(); Reflex::Interval->new( interval => 1, on_tick => [ $self, "callback" ], ) }, );
sub callback { print "method got tick...\n" }}
• Thing->new()->run_all();
Subclassed Interval
{• package TickingThing;
use Moose;• extends "Reflex::Interval";
before on_tick => sub { print "customized tick...\n" };}
TickingThing->new( interval => 1 )->run_all();
Subclassed Interval
{ package TickingThing; use Moose; extends "Reflex::Interval";
• before on_tick => sub {• print "customized tick...\n"
};}
TickingThing->new( interval => 1 )->run_all();
Subclassed Interval
{ package TickingThing; use Moose; extends "Reflex::Interval";
before on_tick => sub { print "customized tick...\n" };}
• TickingThing->new( interval => 1 )->run_all();
Roles
Roles
• They’re sticky.
• They’re delicious.
• They’re high in carbohydrate calories.
• They may contain bacon.
Reflex Roles
• Eventy features are implemented as roles.
• Consume them when appropriate.
• Each role has a corresponding class.
• Reflex::Interval is Reflex::Role::Interval.
Reflex::Interval (1 of 3)
package Reflex::Interval;use Moose; extends "Reflex::Base";
• has interval => ( isa => "Num", is => "rw");
• has auto_repeat => ( isa => "Bool", is => "ro", default => 1);
• has auto_start => ( isa => "Bool", is => "ro", default => 1);
Reflex::Interval (2 of 3)
with "Reflex::Role::Interval" => {• att_interval => "interval",• att_auto_start => "auto_start",• att_auto_repeat => "auto_repeat",
cb_tick => "on_tick", method_start => "start", method_stop => "stop", method_repeat => "repeat",};
sub on_tick { my ($self, $args) = @_; $self->emit( event => "tick", args => $args );}
Reflex::Interval (2 of 3)
with "Reflex::Role::Interval" => { att_interval => "interval", att_auto_start => "auto_start", att_auto_repeat => "auto_repeat",
• cb_tick => "on_tick", method_start => "start", method_stop => "stop", method_repeat => "repeat",};
• sub on_tick { my ($self, $args) = @_;
• $self->emit(• event => "tick", args => $args
);}
Reflex::Interval (2 of 3)
with "Reflex::Role::Interval" => { att_interval => "interval", att_auto_start => "auto_start", att_auto_repeat => "auto_repeat", cb_tick => "on_tick",
• method_start => "start",• method_stop => "stop",• method_repeat => "repeat",
};
sub on_tick { my ($self, $args) = @_; $self->emit( event => "tick", args => $args );}
1;Reflex::Interval (3 of 3)
Parameterized Roles
• Reflex uses role parameters to wire together code.
• MooseX::Role:: Parameterized rocks for this.
Three Kinds of Role Parameters
Attribute Parameters
• Name attributes in the consumer that control role behavior.
• Begin with “att_”.
Attribute Parameterspackage Reflex::Interval;use Moose;
• has interval => ( isa => "Num", ... );• has auto_start => ( isa => "Bool", ... );
...;
with "Reflex::Role::Interval" => { att_interval => "interval", att_auto_start => "auto_start", ...,};
Attribute Parameterspackage Reflex::Interval;use Moose;
has interval => ( isa => "Num", ... );has auto_start => ( isa => "Bool", ... );...;
with "Reflex::Role::Interval" => {• att_interval => "interval",• att_auto_start => "auto_start",
...,};
Callback Parameters
• Name consumer methods to call back when things happen.
• Begin with “cb_”.
• These callbacks are simple, synchronous method calls.
Callback Parameterspackage Reflex::Interval;use Moose;...;
with "Reflex::Role::Interval" => {• cb_tick => "on_tick",
...,};
• sub on_tick { my ($self, $args_hash) = @_; ...;}
Method Parameters
• Roles may implement public API methods.
• Classes get to decide what they’re called.
• Begin with “method_”.
Method Parameters{ package Reflex::Interval; use Moose; extends "Reflex::Base";
with "Reflex::Role::Interval" => { ...,
• method_start => "start",• method_stop => "stop",
};}
my $interval = Reflex::Interval->new();• $interval->stop();• $interval->start();
Lots of Role
Parameters
Awesome but
tedious to configure.
Dynamic Defaults
• Each role designates a primary attribute parameter.
• Other parameter default values are based on the primary parameter’s value.
• Avoids namespace clashes as an extension of basic OO.
Primary Attributewith "Reflex::Role::Interval" => {
• att_interval => "watchdog", ...,}
Role Parameter Default Name
method_start start_watchdog()
method_stop stop_watchdog()
cb_tick on_watchdog_tick()
Primary Attributewith "Reflex::Role::Interval" => {
• att_interval => "log_rotation", ...,}
Role Parameter Default Name
method_start start_log_rotation()
method_stop stop_log_rotation()
cb_tick on_log_rotation_tick()
Primary Attributewith "Reflex::Role::Interval" => {
• att_interval => "interval",• att_auto_start => "auto_start",• att_auto_repeat => "auto_repeat",• cb_tick => "on_tick",• method_start => "start",• method_stop => "stop",• method_repeat => "repeat",
};
Redundancy is a special case.It overrides generally useful defaults.
Primary Attributewith "Reflex::Role::Interval" => {
• att_interval => "interval", att_auto_start => "auto_start", att_auto_repeat => "auto_repeat",
• cb_tick => "on_tick", method_start => "start", method_stop => "stop", method_repeat => "repeat",};
Calls $self->on_tick()... not $self->on_interval_tick()
Primary Attributewith "Reflex::Role::Interval" => {
• att_interval => "interval", att_auto_start => "auto_start", att_auto_repeat => "auto_repeat", cb_tick => "on_tick",
• method_start => "start", method_stop => "stop", method_repeat => "repeat",};
Creates $interval->start()... not $interval->start_interval()
Primary Attributewith "Reflex::Role::Interval" => {
• att_interval => "interval", att_auto_start => "auto_start", att_auto_repeat => "auto_repeat", cb_tick => "on_tick", method_start => "start",
• method_stop => "stop", method_repeat => "repeat",};
Creates $interval->stop()... not $interval->stop_interval()
Primary Attributewith "Reflex::Role::Interval" => {
• att_interval => "interval", att_auto_start => "auto_start", att_auto_repeat => "auto_repeat", cb_tick => "on_tick", method_start => "start", method_stop => "stop",
• method_repeat => "repeat",};
Etc.
Other Moose Magic
Expose Inner Workings
Expose Inner Workings{
• package AnotherTickingThing;• use Moose; extends "TickingThing";
has "+ticker" => ( handles => [ "interval" ], );}
my $thing = AnotherTickingThing->new();$thing->interval(0.1);$thing->run_all();
Expose Inner Workings{ package AnotherTickingThing; use Moose; extends "TickingThing";
• has "+ticker" => (• handles => [ "interval" ],• );
}
my $thing = AnotherTickingThing->new();$thing->interval(0.1);$thing->run_all();
Expose Inner Workings{ package AnotherTickingThing; use Moose; extends "TickingThing";
has "+ticker" => ( handles => [ "interval" ], );}
my $thing = AnotherTickingThing->new();• $thing->interval(0.1);
$thing->run_all();
Replace Inner Workings
Replace Inner Workings
my $thing = TickingThing->new();
• $thing->ticker(• Reflex::Interval->new(• interval => 0.125,
on_tick => [ $thing, "callback" ], ));
$thing->run_all();
• my $thing = TickingThing->new( ticker => Reflex::Interval->new( interval => 0.125,
• on_tick => [ $thing, "callback" ], ));
Replace Inner Workings
Can’t use $thing like this.It must be declared before it can be used.
Two statements.Reference to $thing held inside $thing.
Replace Inner Workings
• my $thing;• $thing = TickingThing->new(
ticker => Reflex::Interval->new( interval => 0.125,
• on_tick => [ $thing, "callback" ], ));
• my $thing = TickingThing->new();
• $thing->ticker( Reflex::Interval->new( interval => 0.125,
• on_tick => [ $thing, "callback" ], ));
Dynamically replace it later.Reference to $thing held inside $thing.
Replace Inner Workings
my $thing = TickingThing->new();
$thing->ticker( Reflex::Interval->new( interval => 0.125,
• on_tick => [ $thing, "callback" ], ));
Either way, Reflex automatically weakens the inner reference for you.
Replace Inner Workings
Override Attributes
Override Attributes
{• package FasterInterval;• use Moose; extends "Reflex::Interval";
has "+interval" => ( default => 0.5, );}
FasterInterval->new()->run_all();
Override Attributes
{ package FasterInterval; use Moose; extends "Reflex::Interval";
• has "+interval" => (• default => 0.5,
);}
FasterInterval->new()->run_all();
Override Attributes
{ package FasterInterval; use Moose; extends "Reflex::Interval";
has "+interval" => ( default => 0.5, );}
• FasterInterval->new()->run_all();
Whatever Objects Can Do
What Can’t Objects Do?
☺
ReflexHow does it work?
Rocco Caputo – @rcaputoYAPC::NA
Tuesday, 28 June 2011Around Teatime
Static Reflex=
Moose
Dynamic Reflex
=Emit & Watch
Crossing the Barrier Between Static & Dynamic
Interaction
Static to Dynamic
• Classes are built using static roles.
• Classes implement additional features.
• Callbacks within a class are synchronous.
• Classes emit dynamic messages.
• Dynamic object interaction is based on watching for those messages.
Default Callbackwith "Reflex::Role::Interval" => { att_interval => "interval", att_auto_start => "auto_start", att_auto_repeat => "auto_repeat",
• cb_tick => "on_tick", method_start => "start", method_stop => "stop", method_repeat => "repeat",};
• sub on_tick { my ($self, $args) = @_;
• $self->emit(• event => "tick", args => $args
);}
Dynamic back to Static?
Just Call Stuff, Okay?
Trade Offs
• Dynamic object interaction is richer, more fun, and often necessary.
• Beware of using dynamic messages solely “for fun’s sake”.
• Static callbacks are more efficient.
Emitting an Event• package Moosian; use Moose;• extends "Reflex::Base";
has name => ( is => "ro", isa => "Str" );
sub yip { my ($self, $args) = @_; print( $self->name(), " says: Yip-yip-yip-yip..", " uh-huh uh-huh..\n" ); $self->emit( event => "yipped" );}
1;
Emitting an Eventpackage Moosian; use Moose;extends "Reflex::Base";
has name => ( is => "ro", isa => "Str" );
• sub yip { my ($self, $args) = @_;
• print(• $self->name(),• " says: Yip-yip-yip-yip..",• " uh-huh uh-huh..\n"• );
$self->emit( event => "yipped" );}
1;
Emitting an Eventpackage Moosian; use Moose;extends "Reflex::Base";
has name => ( is => "ro", isa => "Str" );
sub yip { my ($self, $args) = @_; print( $self->name(), " says: Yip-yip-yip-yip..", " uh-huh uh-huh..\n" );
• $self->emit( event => "yipped" );}
1;
Emitting Events
• Objects emit() events as part of their public interfaces.
• Events have no explicit destination.
• It’s up to users to watch() for events.
Watching Events
• my $bob = Moosian->new( name => "Bob" );my $joe = Moosian->new( name => "Joe" );
• $bob->watch( $joe, "yipped", "yip" );$joe->watch( $bob, "yipped", "yip" );
$bob->yip();
Reflex->run_all();
Watching Events
my $bob = Moosian->new( name => "Bob" );• my $joe = Moosian->new( name => "Joe" );
$bob->watch( $joe, "yipped", "yip" );• $joe->watch( $bob, "yipped", "yip" );
$bob->yip();
Reflex->run_all();
Watching Events
my $bob = Moosian->new( name => "Bob" );my $joe = Moosian->new( name => "Joe" );
$bob->watch( $joe, "yipped", "yip" );$joe->watch( $bob, "yipped", "yip" );
• $bob->yip();
• Reflex->run_all();
Moosian Dialog
Bob says: Yip-yip-yip-yip... uh-huh uh-huh..Joe says: Yip-yip-yip-yip... uh-huh uh-huh..Bob says: Yip-yip-yip-yip... uh-huh uh-huh..Joe says: Yip-yip-yip-yip... uh-huh uh-huh..Bob says: Yip-yip-yip-yip... uh-huh uh-huh..Joe says: Yip-yip-yip-yip... uh-huh uh-huh..Bob says: Yip-yip-yip-yip... uh-huh uh-huh..Joe says: Yip-yip-yip-yip... uh-huh uh-huh..Bob says: Yip-yip-yip-yip... uh-huh uh-huh..Joe says: Yip-yip-yip-yip... uh-huh uh-huh..^C
Infinite Recursion OK
TIME RSS COMMAND• 1:16.60 17940 perl moosians.pl
TIME RSS COMMAND 2:37.94 17940 perl moosians.pl
TIME RSS COMMAND 3:05.86 17940 perl moosians.pl
TIME RSS COMMAND 3:30.69 17940 perl moosians.pl
Infinite Recursion OK
TIME RSS COMMAND 1:16.60 17940 perl moosians.pl
TIME RSS COMMAND• 2:37.94 17940 perl moosians.pl
TIME RSS COMMAND 3:05.86 17940 perl moosians.pl
TIME RSS COMMAND 3:30.69 17940 perl moosians.pl
Infinite Recursion OK
TIME RSS COMMAND 1:16.60 17940 perl moosians.pl
TIME RSS COMMAND 2:37.94 17940 perl moosians.pl
TIME RSS COMMAND• 3:05.86 17940 perl moosians.pl
TIME RSS COMMAND 3:30.69 17940 perl moosians.pl
Infinite Recursion OK
TIME RSS COMMAND 1:16.60 17940 perl moosians.pl
TIME RSS COMMAND 2:37.94 17940 perl moosians.pl
TIME RSS COMMAND 3:05.86 17940 perl moosians.pl
TIME RSS COMMAND• 3:30.69 17940 perl moosians.pl
Simplified Hierarchical Watching
Hierarchical Watching?• Most program
structure is hierarchical.
• Trees of objects that use other objects.
• Parent objects often need results from children.
Moose Traits Rock
Watched Attributes
Callbacks Discovered by
Name
• use Reflex::Trait::Watched qw(watches);
• watches clock => ( isa => "Reflex::Interval", setup => sub { Reflex::Interval->new(interval => 1) },);
sub on_clock_tick { print "tick...\n" }
Watched Attributes
use Reflex::Trait::Watched qw(watches);
watches clock => ( isa => "Reflex::Interval", setup => sub { Reflex::Interval->new(interval => 1) },);
• sub on_clock_tick { print "tick...\n" }
Watched Attributes
use Reflex::Trait::Watched qw(watches);
watches clock => ( isa => "Reflex::Interval", setup => sub { Reflex::Interval->new(interval => 1) },);
• sub on_clock_tick { print "tick...\n" }
Watched Attributes
use Reflex::Trait::Watched qw(watches);
• watches clock => ( isa => "Reflex::Interval", setup => sub { Reflex::Interval->new(interval => 1) },);
• sub on_clock_tick { print "tick...\n" }
Watched Attributes
use Reflex::Trait::Watched qw(watches);
watches clock => (• isa => "Reflex::Interval",
setup => sub { Reflex::Interval->new(interval => 1) },);
• sub on_clock_tick { print "tick...\n" }
Watched Attributes
use Reflex::Trait::Watched qw(watches);
• watches penguin => (• isa => "Reflex::Bomb",
setup => sub { ... },);
sub on_penguin_tick { ... }sub on_penguin_stop { ... }sub on_penguin_explode { ... }
Watched Attributes
use Reflex::Trait::Watched qw(watches);
watches penguin => ( isa => "Reflex::Bomb", setup => sub { ... },);
• sub on_penguin_tick { ... }• sub on_penguin_stop { ... }• sub on_penguin_explode { ... }
Watched Attributes
use Reflex::Trait::Watched qw(watches);
• watches watchdog => ( ... Interval ... );• watches log_mark => ( ... Interval ... );
• sub on_watchdog_tick { ... }• sub on_log_mark_tick { ... }
Watched Attributes
Reflex Role Composition
Two-Way Pipe Driver
• Bidirectionally stream data between two file handles.
• Useful for proxies.
Two File Handles• package Proxy;
use Moose;extends "Reflex::Base";
• has client => (• isa => "FileHandle",
is => "rw", required => 1 );
• has server => (• isa => "FileHandle",
is => "rw", required => 1 );
• has active => (• isa => "Bool",
is => "ro", default => 1 );
Reduced for Example
• use Reflex::Callbacks "make_null_handler";
• make_null_handler("on_client_closed");• make_null_handler("on_client_error");
• make_null_handler("on_server_closed");• make_null_handler("on_server_error");
From Client to Server
• with "Reflex::Role::Streaming" => { att_active => "active",
• att_handle => "client",};
sub on_client_data { my ($self, $arg) = @_; $self->put_server($arg->{data});}
From Client to Server
with "Reflex::Role::Streaming" => { att_active => "active", att_handle => "client",};
• sub on_client_data { my ($self, $arg) = @_;
• $self->put_server($arg->{data});}
From Server to Client
• with "Reflex::Role::Streaming" => { att_active => "active",
• att_handle => "server",};
sub on_server_data { my ($self, $arg) = @_; $self->put_client($arg->{data});}
From Server to Client
with "Reflex::Role::Streaming" => { att_active => "active", att_handle => "server",};
• sub on_server_data { my ($self, $arg) = @_;
• $self->put_client($arg->{data});}
1;We’re “Done”
Quote-Done-Unquote
• Not handling EOF (cb_closed).
• Not handling errors (cb_error).
• We’ll probably need separate “active” flags for client and server later.
• Flow-control for data rate mismatches?
• Etc.
Usage
# Assume we already have the sockets.
• my $p = Proxy->new(• client => $client_socket,• server => $server_socket,
);
# Do something else while it runs.
Simple?
I Wrote What?
on_client_dataon_client_closedon_client_errorput_clientstop_clienton_server_dataon_server_closedon_server_errorput_serverstop_server
clientserveractive
Proxy
att_handleatt_activecb_datacb_closedcb_errormethod_putmethod_stop
Reflex::Role::Streaming
cb_readymethod_pausemethod_resumemethod_startmethod_stop
att_handleatt_active
Reflex::Role::Writable
cb_errormethod_putmethod_flush
att_handleReflex::Role::Writing
cb_readymethod_pausemethod_resumemethod_stop
att_handleatt_active
Reflex::Role::Readable
cb_closedcb_datacb_errormethod_read
att_handleReflex::Role::Reading
att_handleatt_activecb_datacb_closedcb_errormethod_putmethod_stop
Reflex::Role::Streaming
cb_readymethod_pausemethod_resumemethod_startmethod_stop
att_handleatt_active
Reflex::Role::Writablecb_errormethod_putmethod_flush
att_handleReflex::Role::Writing
cb_readymethod_pausemethod_resumemethod_stop
att_handleatt_active
Reflex::Role::Readable
cb_closedcb_datacb_errormethod_read
att_handleReflex::Role::Reading
Reflex::Role::StreamingReflex Role The ability to....
Reading ... read data from a non-blocking file handle.
Readable ... watch for data arriving on a file handle.
Writing ... buffer and/or write data to a NB file handle.
Writable ... watch a file handle for ability to write data.
Relax–It’s Just the Class
• That’s the Proxy class.
• All instances will share the same code.
Overkill? Dial it Back!
Roles let programs pick and mix what
they need.
Reflex::Role::InStreaming
att_handleatt_activecb_datacb_closedcb_errormethod_stop
Reflex::Role::InStreaming
cb_readymethod_pausemethod_resumemethod_stop
att_handleatt_active
Reflex::Role::Readable
cb_closedcb_datacb_errormethod_read
att_handleReflex::Role::Reading
Reflex::Role::OutStreaming
att_handleatt_activecb_datacb_closedcb_errormethod_putmethod_stop
Reflex::Role::OutStreaming
cb_readymethod_pausemethod_resumemethod_startmethod_stop
att_handleatt_active
Reflex::Role::Writable
cb_errormethod_putmethod_flush
att_handleReflex::Role::Writing
Dynamic Fun
SmallTalk-Like Messaging
Auto-Emit Messages When
Attributes Change
• package EmittingCounter;
• use Reflex::Trait::EmitsOnChange qw(emits);
emits count => ( isa => "Int", default => 0);
sub something_happens { my $self = shift; $self->count($self->count() + 1);}
SmallTalk Messaging
package EmittingCounter;
use Reflex::Trait::EmitsOnChange qw(emits);
• emits count => ( isa => "Int", default => 0);
sub something_happens { my $self = shift;
• $self->count($self->count() + 1);}
SmallTalk Messaging
Now Watch It• use EmittingCounter;
use Reflex::Trait::Watched qw(watches);
• watches counter => (• isa => "EmittingCounter",
setup => sub { EmittingCounter-> new() },);
sub on_counter_count { my ($self, $args) = @_; print "counter reached $args->{value}\n";}
Now Watch Ituse EmittingCounter;use Reflex::Trait::Watched qw(watches);
watches counter => ( isa => "EmittingCounter", setup => sub { EmittingCounter-> new() },);
• sub on_counter_count { my ($self, $args) = @_;
• print "counter reached $args->{value}\n";}
Self-Managed
Collections
Reflex::Collection
• Manages objects that do Reflex::Role::Collectible.
• Collectible objects are automatically cleaned up when they stop.
• Great for create-and-forget things.
Echo Server (1 of 1)
{• package TcpEchoServer; use Moose;• extends "Reflex::Acceptor"; use EchoStream;
use Reflex::Collection qw(has_many);
has_many clients => ( handles => { remember_client => "remember" } );
sub on_accept { my ($self, $args) = @_; $self->remember_client( EchoStream->new( handle => $args->{socket} ) ); }}
Echo Server (1 of 1)
{ package TcpEchoServer; use Moose;
• extends "Reflex::Acceptor"; use EchoStream; use Reflex::Collection qw(has_many);
has_many clients => ( handles => { remember_client => "remember" } );
sub on_accept { my ($self, $args) = @_; $self->remember_client( EchoStream->new( handle => $args->{socket} ) ); }}
Echo Server (1 of 1)
{ package TcpEchoServer; use Moose; extends "Reflex::Acceptor"; use EchoStream;
• use Reflex::Collection qw(has_many);
has_many clients => ( handles => { remember_client => "remember" } );
sub on_accept { my ($self, $args) = @_; $self->remember_client( EchoStream->new( handle => $args->{socket} ) ); }}
Echo Server (1 of 1)
{ package TcpEchoServer; use Moose; extends "Reflex::Acceptor"; use EchoStream; use Reflex::Collection qw(has_many);
• has_many clients => (• handles => {• remember_client => "remember"
} );
sub on_accept { my ($self, $args) = @_; $self->remember_client( EchoStream->new( handle => $args->{socket} ) ); }}
{ package TcpEchoServer; use Moose; extends "Reflex::Acceptor"; use EchoStream; use Reflex::Collection qw(has_many);
has_many clients => ( handles => { remember_client => "remember" } );
• sub on_accept { my ($self, $args) = @_;
• $self->remember_client(• EchoStream->new( handle => $args->{socket} )
); }}
Echo Server (1 of 1)
EchoStream (1 of 1)
• package EchoStream;use Moose;
• extends "Reflex::Stream";
sub on_data { my ($self, $args) = @_; $self->put($args->{data});}
1;
EchoStream (1 of 1)
package EchoStream;use Moose;extends "Reflex::Stream";
• sub on_data { my ($self, $args) = @_;
• $self->put($args->{data});}
1;
POE Compatibility
Benefits of POE
• Mature and stable.
• Use any event loop you want.
• Hundreds of existing modules.
• Thousands of users.
• And you.
• Don’t rewrite everything at once.
POE Compatibility
• Migrate incrementally.
• Reflex can talk to POE modules.
• POE modules can talk to Reflex objects.
• “Separate but equal” is not enough.
• Static Reflex is faster than POE message passing.
• Reflex eg directory contains examples.
“But is Reflex Ready?”
40% Complete!
• Large swathes of design are stable.
• Documentation!
• ... which I broke preparing for this talk.
• Bugs!
• ... which I added preparing for this talk.
• Roadmap documented in the repository.
Help Make It Better
• http://github.com/rcaputo/reflex
• See the roadmap.
• #reflex on irc.perl.org
• Hackathon, BOF or hallway track.
• Use it, and complain... to me.
• Influence the project while it’s still plastic.
Contribute to a Project• Nick Perez’s Reflex-based psgi server.
• Reflexive::Role::CollectiveInteract with a collection of reflexive objects.
• Reflexive::Role::DataMoverMove data between two streams.
• Reflexive::Role::TCPServerBecome a fully featured TCP server.
• Reflexive::Stream::FilteringApply POE filters to Reflex streams.
• (Your Project Here)
Thank you!
WebliographyDrawing of Edgar Allan Poe.
“A Softer World” comic.
Wizard Moose
Edgar Allan Bro
Universum
Tiled Angry Moose
Accidental Goatse (Unused)
Self-Herding Cat
ORM Diagram
POE's users are outstanding in their fields.
Moose Antlers
Promise Ring
The Watchers
A Fighter Jet Made of Biceps