https://gist.github.com/iarna/8625596
With 10,000 entries in @set.
1..1
Benchmark: timing 1000 iterations of ForLoop, ListMoreUtils...
ForLoop: 11 wallclock secs (10.16 usr + 0.04 sys = 10.20 CPU) @ 98.04/s (n=1000)
ListMoreUtils: 4 wallclock secs ( 4.37 usr + 0.01 sys = 4.38 CPU) @ 228.31/s (n=1000)
ok 1 - Do they match
With 100,000 entries in @set.
1..1
Benchmark: timing 100 iterations of ForLoop, ListMoreUtils...
ForLoop: 11 wallclock secs (11.18 usr + 0.04 sys = 11.22 CPU) @ 8.91/s (n=100)
ListMoreUtils: 4 wallclock secs ( 3.49 usr + 0.02 sys = 3.51 CPU) @ 28.49/s (n=100)
ok 1 - Do they match
With 1,000,000 entries in @set.
1..1
Benchmark: timing 10 iterations of ForLoop, ListMoreUtils...
ForLoop: 14 wallclock secs (13.17 usr + 0.15 sys = 13.32 CPU) @ 0.75/s (n=10)
ListMoreUtils: 5 wallclock secs ( 4.42 usr + 0.13 sys = 4.55 CPU) @ 2.20/s (n=10)
ok 1 - Do they match
So yeah, the smarty pants version scales better at every scale.
Another option is http://git-annex.branchable.com/ which supports encrypted backends. In fact, your use cases sound like what it was designed for.
...
if (caller()) {
# demo code section.
my $ack = sub { $_[0] ? 'yep' : 'nope' };
... $ack->(MyModule::does_this()) ...
}
The only time I'd use an eval or mucking with globs like above is if I were doing something like injecting a mock subroutine into code that wasn't otherwise very testable.
]]>Naming wise, it does actually use Sub::Exporter so you can rename it if you have conflicts. I need to mention that in my docs, however.
@joel: So yeah, if I can find another name that means what it says well enough, I'd prefer to avoid the cognitive clash. Among the thoughts I've had are: "sync" and "waitfor". I'm not super keen on "sync" because it's mostly wrong, if you trigger more then one event. I like "waitfor" a bit better, although it doesn't capture the idea that we're getting the result of the callback. So I'll have to think about this more... (and I'm open to suggestions).
]]>It adds a little command to make calling async APIs in a synchronous, but non-blocking manner easy. Let’s start with an example of how you might do this without my shiny new module:
use AnyEvent::Socket qw( inet_aton );
my $cv = AE::cv;
inet_aton( 'localhost', sub { $cv->send(@_) });
my @ips = $cv->recv;
say join ".", unpack("C*") for @ips;
The above is not an uncommon pattern when using AnyEvent, especially in libraries, where your code should block, but you don’t want to block other event listeners. AnyEvent::Capture makes this pattern a lot cleaner:
use AnyEvent::Capture;
use AnyEvent::Socket qw( inet_aton );
my @ips = capture { inet_aton( 'localhost', shift ) };
say join ".", unpack("C*") for @ips;
The AnyEvent::DBus documentation provides another excellent example of just how awkward this can be:
use AnyEvent;
use AnyEvent::DBus;
use Net::DBus::Annotation qw(:call);
my $conn = Net::DBus->find; # always blocks :/
my $bus = $conn->get_bus_object;
my $quit = AE::cv;
$bus->ListNames (dbus_call_async)->set_notify (sub {
for my $name (@{ $_[0]->get_result }) {
print " $name\n";
}
$quit->send;
});
$quit->recv;
With AnyEvent::Capture this would be:
use AnyEvent;
use AnyEvent::Capture;
use AnyEvent::DBus;
use Net::DBus::Annotation qw(:call);
my $conn = Net::DBus->find; # always blocks :/
my $bus = $conn->get_bus_object;
my $reply = capture { $bus->ListNames(dbus_call_async)->set_notify(shift) };
for my $name (@{ $reply->get_result }) {
print " $name\n";
}
We can also find similar examples in the Coro documentation, where rouse_cb/rouse_wait replace condvars:
sub wait_for_child($) {
my ($pid) = @_;
my $watcher = AnyEvent->child (pid => $pid, cb => Coro::rouse_cb);
my ($rpid, $rstatus) = Coro::rouse_wait;
$rstatus
}
Even still, for the common case, AnyEvent::Capture provides a much cleaner interface, especially as it will manage the guard object for you.
sub wait_for_child($) {
my ($pid) = @_;
my($rpid, $rstatus) = capture { AnyEvent->child (pid => $pid, cb => shift) };
$rstatus
}
]]>
@Paul— Yes, exactly. Huh, IO::Async is interesting, do you have a page or a post somewhere explaining how you’d like it to fit into the wider Perl event loop/async programming module world? I’m having trouble figuring out what it’s place is – it seems to overlap a number of different projects, so I’m curious what your goals are with it.
]]>I know if I were to be doing it universally in Perl I'd be pretty quick to write a little Devel::Declare module to hide away all of the scaffolding. Assuming at least, that someone hasn't already done this.
]]>When fatpacker goes to analyze a module list, it at one stage runs require on all of them, like so:
require $_ for @packages;
Then later on it uses @packages and discovers that one of the elements has is now undef. How did this happen?
Well, if the module you require fiddles with $_ without localizing it first, that will ultimately result in modifying @packages. How did Digest::Perl::MD5 do this?
while (<DATA>) {
It read its data block at load load time. And of course, the last value in $_ there is undef. This all would have been avoided had the require loop been written out to be less golfy:
for my $package (@packages) {
require $package;
}
Of course, this is the same function that had:
my %found;@found{map+($pack_rev{$INC{$_}}||()),@targets}=();
And anyone who would publish code with that in it is probably beyond hope. ;)
(Bugs with patches have been filed with both App::FatPacker and Digest::Perl::MD5. The latter, to just localize $_ before the while loop.)
]]>But, said I, its calling convention is the rather gross Coro::LocalScalar->new->localize($scalar);
and it’s implemented using tie and only for scalar values. It would be nice to have a new keyword and support for arrays and hashes as well. As it happens, I’d also recently run across Begin::Declare, which is a Devel::Declare based module that provides custom variable declarations.
A few hours later, with the example of Begin::Declare to work from, I bring you Coro::Localize (now on a CPAN mirror near you). It’s localization is achieved through a combination of Coro’s on_enter /on_leave blocks and Data::Alias.
So you might use it this way:
our $scalar = "some value";
async {
corolocal $scalar = "thread local";
...
};
$scalar
will be set to "thread local"
for all of the code inside the async block, even if you cede and someone else fiddles with the variable. Similarly, any code outside the async block will only ever see "some value"
.