The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Evo::Lib

VERSION

version 0.0405

FUNCTIONS

steady_time

Return an array contains only uniq elements

uniq(@args)

Return an array contains only uniq elements

strict_opts($level, $hash, @keys)

sub foo3(%opts) { strict_opts(\%opts, qw(foo bar)); }
sub foo2(%opts) { strict_opts(\%opts, [qw(foo bar)], 1); }

Get a $hash and return values in order defined by @keys. If there are superfluous keys in hash, throw an error. This will help you to protect your functions from bugs "passing wrong keys"

$level (the last argument if the second is array ref) determines how many frames to skip. By default it's 1

try

The behaviour is just like JS's try catch finally with one exception: return statement in finally block doesn't matter, because in perl every subroutine returns something (and because it's more "as expected") and it't impossible to distinguish return/no return

use Evo '-Lib try';

# try + catch
try { die "MyError" } sub($e) { say "Catched: $e" };

# try + catch + finally
try sub { die "MyError" }, sub($e) { say "Catched: $e" }, sub { say "Finally" };

Firstly "try_fn" will be executed. If it throws an error, "catch_fn" will be executed with that exception as an argument and perl won't die. "finally_fn", if exists, will be always executed but the return value of finally_fn will be ignored.

Examples

# fin; result: ok
my $res = try sub { return "ok" }, sub {...}, sub { print "fin; " };
say "result: ", $res;

# fin; result: catched
$res = try sub { die "Error\n" }, sub { return "catched" }, sub { print "fin; " };
say "result: ", $res;

"Catch" block can be skipped if we're interesting only in "finally"

# print fin than dies with "Error" in $@
$res = try sub { die "Error\n" }, undef, sub { print "fin\n" };

If "finally" fn throws an exception, it will be rethrown as expected

# die in finally block with "FinError\n" in $@
$res = try sub { 1 }, sub {...}, sub { die "FinError\n" };

Deals correctly with wantarray

# 1;2;3
local $, = ';';
say try sub { wantarray ? (1, 2, 3) : 'One-Two-Three' }, sub {...};

# One-Two-Three
say scalar try sub { wantarray ? (1, 2, 3) : 'One-Two-Three' }, sub {...};

Also you should pay attention that it doesn't localize $@. That's intentionally

Motivation

There is a similar and popular Try::Tiny, but it has a flaw: it can't catch errors in finally block. Which isn't unacceptable for module written for exception handling

use Evo 'Try::Tiny; -Lib try:evo_try; Test::More';

# try tiny can't catches errors here because of flaw in module design
eval {
  try {} catch { } finally { die "foo\n" };
};
is $@, "foo\n", "Try::Tiny";

# Evo's try catches an error correctly
eval {
  evo_try sub { }, sub { }, sub { die "foo\n" };
};
is $@, "foo\n", "Evo";

Also this module is much faster (both PP and XS) and more tiny(~30 lines of code in PP). The only disadvantage is (is it?) it doesn't simulate "catch, finally" keyword. But for me it's not a problem

eval_want

Invokes a last argument with the context of the first void: undef, scalar: '', list: 1, passing remaining arguments. If the function throws an error, returns nothing and sets <$@>. So returned value can answer the question was an invocation successfull or not

Mostly for internal purposes to deal correctly with wantarray and for creating a spy. Short: it allows to intercept execution flow without loosing a context

use Evo '-Lib eval_want';

sub add2($val) { $val + 2 }

sub create_spy($fn) {
  sub {
    warn "[spy] context: ", wantarray,  "; args: ", join ';', @_;
    my $call = eval_want(wantarray, @_, $fn) or die $@;
    $call->();
  };
}

my $spy = create_spy(\&add2);
say $spy->(10);

As you can se, we just pass given context wantarray to the eval_want

AUTHOR

alexbyk.com

COPYRIGHT AND LICENSE

This software is copyright (c) 2016 by alexbyk.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.