Most asynchronous Perl programming is unnecessarily dynamic. It conflicts with object orientation, and it reintroduces memory management issues that some of us learned Perl to escape.
Reflex is a flexible, contemporary asynchronous library that embraces the latest developments in Perl object orientation. Asynchronous classes can be snapped together at coding time, reducing the amount of anonymous code that's often slung around at runtime. Methods are first-class callbacks in Reflex; they can be augmented and overridden using normal Perl OO. Mixing in Moose make things even better.
This presentation is an expanded, more code-intensive version of my Perl Oasis talk. It will cover the 6.5 ways Reflex-based modules can be used, including anonymous callback and closure juggling, and mind-bogglingly powerful Moose-fueled OO crack. Imperative promises are included for people who just want to do one simple thing without mucking about with callbacks at all.
4. 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
10. The Other Committers
• Adam Kennedy • Jonathan Steinert
• Benjamin Smith • Larwan Berke
• Casey West • Martijn van Beers
• Chris Fedde • Matt Cashner
• Chris Williams • Matt Sickler
• David Davis • Perl Whore
• David Webb • Philip Gwyn
• Hinrik Örn Sigurðsson • Tom Feist
• jmadler • Yuval Kogman
11. The CPAN Authors
Alejandro 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 • wb@95700.net •
William Travis Holton • Yuji Suzuki • Yves Blusseau • Zoffix Znet
27. 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
28. 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.
39. 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.
44. 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.
52. Static Composition
• Code is bolted together before running.
• Subclassing.
• Role composition.
• All pieces built and destroyed together.
• Most eventy abstractions ignore this.
53. Dynamic Composition
• Event watchers are created,
• related (has-a),
• communicated (with),
• and destructicated
• at run time.
• Lather, rinse, repeat until the owner ends.
54. 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.
64. Ye Olde Coderef - The Good
• Quick and dead simple to use.
• Convenient and fast for small things.
• Parsimonious use of memory.
65. 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.
66. 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 {
73. Methods – The Costs
• Verbose syntax.
• Perl OO is slower than code references
and closures.
• Perl OO requires memory.
• Moose uses memory, too.
74. 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.
75. 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.
78. What’s a Promise?
“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
80. 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.
81. Timer Promise (1 of 2)
use Reflex::Interval;
• my $one = Reflex::Interval->new(
• interval => 1
• );
my $two = Reflex::Interval->new(
interval => 2
);
82. Timer Promise (1 of 2)
use Reflex::Interval;
my $one = Reflex::Interval->new(
interval => 1
);
• my $two = Reflex::Interval->new(
• interval => 2
• );
85. Eventy Timer Promise
% perl promises.pl
• Before : 1295045065
Blocked 2 seconds.
• After two: 1295045067
After one: 1295045067
86. Eventy Timer Promise
% perl promises.pl
Before : 1295045065
• After two: 1295045067
Instant result.
• After one: 1295045067
87. 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.
88. “We were somewhere
around Asheville in the
heart of the Blue
Ridge Mountains when
the Moose began to
take hold.”
98. Roles
• They’re sticky.
• They’re delicious.
• They’re high in carbohydrate calories.
• They may contain bacon.
99. Reflex Roles
• Eventy features are implemented as roles.
• Consume them when appropriate.
• Each role has a corresponding class.
• Reflex::Interval is Reflex::Role::Interval.
100. 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
);
108. Attribute Parameters
package 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",
...,
};
109. Attribute Parameters
package 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",
...,
};
110. Callback Parameters
• Name consumer methods to call back
when things happen.
• Begin with “cb_”.
• These callbacks are simple, synchronous
method calls.
111. Callback Parameters
package Reflex::Interval;
use Moose;
...;
with "Reflex::Role::Interval" => {
• cb_tick => "on_tick",
...,
};
• sub on_tick {
my ($self, $args_hash) = @_;
...;
}
112. Method Parameters
• Roles may implement public API methods.
• Classes get to decide what they’re called.
• Begin with “method_”.
116. 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.
117. Primary Attribute
with "Reflex::Role::Interval" => {
• att_interval => "watchdog",
...,
}
Role Parameter Default Name
method_start start_watchdog()
method_stop stop_watchdog()
cb_tick on_watchdog_tick()
118. Primary Attribute
with "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()
119. Primary Attribute
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",
};
Redundancy is a special case.
It overrides generally useful defaults.
131. Replace Inner Workings
• my $thing = TickingThing->new(
ticker => Reflex::Interval->new(
interval => 0.125,
• on_tick => [ $thing, "callback" ],
)
);
Can’t use $thing like this.
It must be declared before it can be used.
132. Replace Inner Workings
• my $thing;
• $thing = TickingThing->new(
ticker => Reflex::Interval->new(
interval => 0.125,
• on_tick => [ $thing, "callback" ],
)
);
Two statements.
Reference to $thing held inside $thing.
133. Replace Inner Workings
• 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.
134. 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.
145. 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.
148. 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.
149. 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;
150. 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;
151. 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;
152. 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.
153. 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();
154. 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();
155. 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();
156. 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
157. 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
158. 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
159. 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
160. 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
162. Hierarchical Watching?
• Most program
structure is
hierarchical.
• Trees of objects
that use other
objects.
• Parent objects
often need results
from children.
175. Two-Way Pipe Driver
• Bidirectionally stream data between two
file handles.
• Useful for proxies.
176. 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 );
177. 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");
178. 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});
}
179. 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});
}
180. 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});
}
181. 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});
}
183. 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.
184. Usage
# Assume we already have the sockets.
• my $p = Proxy->new(
• client => $client_socket,
• server => $server_socket,
);
# Do something else while it runs.
187. Reflex::Role::Streaming
Reflex
The ability to....
Role
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.
188. Relax–It’s Just the Class
• That’s the Proxy class.
• All instances will share the same code.
189. Overkill? Dial it Back!
Roles let
programs pick
and mix what
they need.
195. 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);
}
196. 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);
}
197. 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";
}
198. 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";
}
200. Reflex::Collection
• Manages objects that do
Reflex::Role::Collectible.
• Collectible objects are automatically
cleaned up when they stop.
• Great for create-and-forget things.
201. 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} )
);
}
}
202. 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} )
);
}
}
203. 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} )
);
}
}
204. 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} )
);
}
}
205. 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} )
);
}
}
206. EchoStream (1 of 1)
• package EchoStream;
use Moose;
• extends "Reflex::Stream";
sub on_data {
my ($self, $args) = @_;
$self->put($args->{data});
}
1;
207. EchoStream (1 of 1)
package EchoStream;
use Moose;
extends "Reflex::Stream";
• sub on_data {
my ($self, $args) = @_;
• $self->put($args->{data});
}
1;
209. 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.
210. 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.
212. 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.
213. Help Make It Better
• http://github.com/rcaputo/reflex
• See the roadmap.
• #reflex on irc.perl.org
• poe-subscribe@perl.org
• Hackathon, BOF or hallway track.
• Use it, and complain... to me.
• Influence the project while it’s still plastic.
214. Contribute to a Project
• Nick Perez’s Reflex-based psgi server.
• Reflexive::Role::Collective
Interact with a collection of reflexive objects.
• Reflexive::Role::DataMover
Move data between two streams.
• Reflexive::Role::TCPServer
Become a fully featured TCP server.
• Reflexive::Stream::Filtering
Apply POE filters to Reflex streams.
• (Your Project Here)
216. Webliography
Drawing of Edgar Allan Self-Herding Cat
Poe.
ORM Diagram
“A Softer World” comic.
POE's users are
Wizard Moose outstanding in their fields.
Edgar Allan Bro Moose Antlers
Universum Promise Ring
Tiled Angry Moose The Watchers
Accidental Goatse A Fighter Jet Made of
(Unused) Biceps