Modules · Language · 1.02

Func::Util

Functional programming utilities, many of them compiled to custom ops.

Memoising, composing, lazy values and safe navigation, with the small checks a program makes all day: is it an array, is it empty, is it between two numbers. Many are custom ops resolved at compile time, so they cost no sub call, and the list searches compare in C with no callback per element.

use Func::Util qw(first_gt dig nvl partition_cb count_cb);

my @orders = map { { id => $_, total => $_ * 40 } } 1 .. 10;

# the first order over 300, compared in C: no callback per element
my $big = first_gt(\@orders, 'total', 300);
print "first over 300: order $big->{id}\n";               # order 8

# safe navigation through nested hashes
my $config = { db => { primary => { host => 'db1' } } };
print dig($config, qw(db primary host)), "\n";            # db1
print nvl(dig($config, qw(db replica host)), 'none'), "\n";   # none

# named predicates, run entirely in C
my ($up, $down) = partition_cb([3, -1, 4, -1, 5], ':is_positive');
print "@$up | @$down\n";                                  # 3 4 5 | -1 -1
print count_cb([0, 2, 7, 10], ':is_even'), " even\n";      # 3 even

What you get

  • Custom ops

    is_array, is_empty, is_between, trim, starts_with, clamp, min2 and the rest compile to custom ops, so a check costs a flag test, not a sub call. identity is removed at compile time.

  • Searches in C

    first_gt, any_eq, all_lt and their kin compare in C, over plain values or a field of each hash, with no Perl callback per element.

  • Named predicates

    any_cb, grep_cb, count_cb and partition_cb take a predicate by name: built-ins such as :is_positive run entirely in C, and register_callback adds your own.

  • Functional tools

    memo, compose, pipeline, partial, lazy and force, tap and once, implemented in XS.

  • Data at hand

    dig walks nested hashes safely, nvl and coalesce pick the first defined value, and pick, omit, pluck, defaults and uniq reshape data.

  • Said plainly

    Its POD says where it does not win: a sub that calls your own code still pays for the call, and calling a partial made once is about 20% slower than a closure written by hand.

In use

use Func::Util qw(memo compose pipeline lazy force register_callback any_cb);

# remembered: the second call with the same argument is a lookup
my $calls = 0;
my $square = memo(sub { $calls++; $_[0] * $_[0] });
$square->(12) for 1 .. 3;
print "square(12) = ", $square->(12), " after $calls call\n";   # 144 after 1 call

# functions joined into one
my $slug = compose(sub { lc $_[0] }, sub { join '-', split ' ', $_[0] });
print $slug->('Hello Big World'), "\n";                  # hello-big-world
print pipeline(5, sub { $_[0] + 1 }, sub { $_[0] * 10 }), "\n";   # 60

# a value computed once, and only if it is asked for
my $config = lazy(sub { print "loading...\n"; +{ mode => 'live' } });
print force($config)->{mode}, "\n";                       # loading... live
print force($config)->{mode}, "\n";                       # live

# a predicate of your own, by name
register_callback('over_100', sub { $_[0] > 100 });
print any_cb([5, 50, 500], 'over_100') ? "one is over 100\n" : '';

Get it

cpanm Func::Util