The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#ABSTRACT: Common helper functions without having to import them
#CONTRIBUTOR: Lukas Atkinson (cpan: AMON) <amon@cpan.org>
#CONTRIBUTOR: Olivier Mengué (cpan: DOLMEN) <dolmen@cpan.org>
use strict;
use version 0.77; our $VERSION = qv('v1.2.0_1');
use overload ();
use Carp ();
use Const::Fast 0.011 ();
use Data::Alias 1.18 ();
use Data::Dump 1.10 ();
use List::MoreUtils 0.07 ();
use List::Util 1.35 ();
use POSIX ();
use Scalar::Util 1.36 ();
use Try::Tiny 0.03 ();
BEGIN {
# check if a competing "_" exists
if (keys %{_::}) {
Carp::confess qq(The package "_" has already been defined);
}
}
BEGIN {
# Load the dummy "_.pm" module.
# This will set up various booby traps so that "_" isn't used directly.
# In order to prevent the traps from triggering when *we* go there, we have
# to declare our peaceful intentions:
local our $_WE_COME_IN_PEACE = 'pinky swear';
require _;
}
our $_ASSIGN_ALIASES;
BEGIN {
$_ASSIGN_ALIASES = sub {
my ($pkg, %aliases) = @_;
no strict 'refs'; ## no critic (ProhibitNoStrict)
while (my ($this, $that) = each %aliases) {
my $target = "_::${this}";
my $source = "${pkg}::${that}";
*{$target} = *{$source}{CODE}
// Carp::croak "Unknown subroutine $source in _ASSIGN_ALIASES";
}
};
}
# From now, every function is in the "_" package
## no critic (ProhibitMultiplePackages)
package # Hide from PAUSE
_;
## no critic (RequireArgUnpacking, RequireFinalReturn, ProhibitSubroutinePrototypes)
# Why this "no critic"? In an util module, efficiency is crucial because we
# have no idea about the context where these function are being used. Therefore,
# no arg unpacking, and no explicit return. Most functions are so trivial anyway
# that this isn't much of a legibility concern.
# Subroutine prototypes are used to offer a convenient and natural interface.
# I fully understand why they shouldn't be used in ordinary code, but this
# module puts them to mostly good use.
# Predeclare a few things so that we can use them in the sub definitions below.
sub blessed(_);
sub ref_type(_);
# load the actual function collections
BEGIN {
$_ASSIGN_ALIASES->(
'Carp',
carp => 'carp',
cluck => 'cluck',
croak => 'croak',
confess => 'confess',
);
}
$_ASSIGN_ALIASES->(
'Try::Tiny',
try => 'try',
catch => 'catch',
finally => 'finally',
);
sub carpf($@) {
my $pattern = shift;
@_ = sprintf $pattern, @_;
goto &carp;
}
sub cluckf($@) {
my $pattern = shift;
@_ = sprintf $pattern, @_;
goto &cluck;
}
sub croakf($@) {
my $pattern = shift;
@_ = sprintf $pattern, @_;
goto &croak;
}
sub confessf($@) {
my $pattern = shift;
@_ = sprintf $pattern, @_;
goto &confess;
}
$_ASSIGN_ALIASES->('Scalar::Util', is_open => 'openhandle');
sub _::prototype ($;$) {
if (@_ == 2) {
goto &Scalar::Util::set_prototype if @_ == 2;
}
if (@_ == 1) {
my ($coderef) = @_;
return prototype $coderef; # Calls CORE::prototype
}
else {
## no critic (RequireInterpolationOfMetachars)
Carp::confess '_::prototype($;$) takes exactly one or two arguments';
}
}
# This sub uses CamelCase because it's a factory function
sub Dir(@) { ## no critic (NamingConventions::Capitalization)
require Path::Class;
Path::Class::Dir->new(@_);
}
# This sub uses CamelCase because it's a factory function
sub File(@) { ## no critic (NamingConventions::Capitalization)
require Path::Class;
Path::Class::File->new(@_);
}
$_ASSIGN_ALIASES->(
'Data::Dump',
pp => 'pp',
dd => 'dd',
);
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Util::Underscore - Common helper functions without having to import them
=head1 VERSION
version v1.2.0_1
=head1 SYNOPSIS
use Util::Underscore;
_::croak "$foo must do Some::Role" if not _::does($foo, 'Some::Role');
=head1 DESCRIPTION
This module contains various utility functions, and makes them accessible through the C<_> package.
This allows the use of these utilities (a) without much per-usage overhead and (b) without namespace pollution.
It contains selected functions from the following modules:
L<Carp|Carp>,
L<Const::Fast|Const::Fast>,
L<Data::Alias|Data::Alias>,
L<Data::Dump|Data::Dump>,
L<List::MoreUtils|List::MoreUtils>,
L<List::Util|List::Util>,
L<POSIX|POSIX>,
L<Scalar::Util|Scalar::Util>,
L<Try::Tiny|Try::Tiny>.
Not all functions from those are available, some have been renamed, and some functions of our own have been added.
=head1 FUNCTION REFERENCE
The function reference is split into separate topics which each have their own documentation:
=over 4
=item Scalars
see L<Util::Underscore::Scalars|Util::Underscore::Scalars>
C<alias>,
C<const>,
C<is_dual>,
C<is_identifier>,
C<is_package>,
C<is_plain>,
C<is_readonly>,
C<is_string>,
C<is_bool>,
C<is_tainted>,
C<is_vstring>,
C<new_dual>
=item Numbers
see L<Util::Underscore::Numbers|Util::Underscore::Numbers>
C<ceil>,
C<floor>,
C<is_int>,
C<is_numeric>,
C<is_uint>
=item References
see L<Util::Underscore::References|Util::Underscore::References>
C<ref_addr>,
C<ref_is_weak>,
C<ref_type>,
C<ref_unweaken>,
C<ref_weaken>
=item Objects
see L<Util::Underscore::Objects|Util::Underscore::Objects>
C<blessed>,
C<can>,
C<class>,
C<class_does>,
C<class_isa>,
C<does>,
C<is_instance>,
C<is_object>,
C<isa>,
C<safecall>
=item List Utils
see L<Util::Underscore::ListUtils|Util::Underscore::ListUtils>
C<all>,
C<any>,
C<each_array>,
C<first>,
C<first_index>,
C<last>,
C<last_index>,
C<max>,
C<max_str>,
C<min>,
C<min_str>,
C<natatime>,
C<none>,
C<pairfirst>,
C<pairgrep>,
C<pairmap>,
C<part>,
C<product>,
C<reduce>,
C<shuffle>,
C<sum>,
C<uniq>,
C<zip>
=item Exception handling
see below
C<carp>,
C<carpf>,
C<catch>,
C<cluck>,
C<cluckf>,
C<confess>,
C<confessf>,
C<croak>,
C<croakf>,
C<finally>,
C<try>
=item Miscellaneous Functions
see below
C<dd>,
C<Dir>,
C<File>,
C<is_open>,
C<pp>,
C<prototype>
=back
=head2 Exception handling
The functions C<_::carp>, C<_::cluck>, C<_::croak>, and C<_::confess> from the C<Carp> module are available.
They all take a list of strings as argument.
How do they differ from each other?
Stack Trace || Fatal | Warning
------------##====================
No || croak | carp
Yes || confess | cluck
How do they differ from Perl's builtin C<die> and C<warn>?
The error messages of C<die> and C<warn> are located on the line where the exception is raised.
This makes debugging hard when the error points to some internal function of a module you are using,
as this provides no information on where your client code made a mistake.
The C<Carp> family of error functions report the error from the point of usage, and optionally provide stack traces.
If you write a module, please use the C<Carp> functions instead of plain C<die>.
Additionally, the variants C<_::carpf>, C<_::cluckf>, C<_::croakf>, and C<_::confessf> are provided.
These take a C<sprintf> patterns as first argument: C<_::carpf "pattern", @arguments>.
To handle errors, the following keywords from C<Try::Tiny> are available:
=over 4
=item *
C<_::try>
=item *
C<_::catch>
=item *
C<_::finally>
=back
They are all direct aliases for their namesakes in C<Try::Tiny>.
=head2 Miscellaneous Functions
=over 4
=item C<$fh = _::is_open $fh>
wrapper for C<Scalar::Util::openhandle>
=item C<$str = _::prototype \&code>
=item C<_::prototype \&code, $new_proto>
gets or sets the prototype, wrapping either C<CORE::prototype> or C<Scalar::Util::set_prototype>
=item C<$dir = _::Dir "foo/bar", "baz">
Creates a new L<Path::Class::Dir|Path::Class::Dir> instance.
=item C<$dir = _::File "foo/bar", "baz.txt">
Creates a new L<Path::Class::File|Path::Class::File> instance.
=back
C<Data::Dump> is an alternative to C<Data::Dumper>.
The main difference is the output format: C<Data::Dump> output tends to be easier to read.
=over 4
=item C<$str = _::pp @values>
wrapper for C<Data::Dump::pp>
=item C<_::dd @values>
wrapper for C<Data::Dump::dd>.
=back
=head1 RATIONALE
=head4 Context and Package Name
There are a variety of good utility modules like C<Carp> or C<Scalar::Util>.
I noticed I don't import these (in order to avoid namespace pollution), but rather refer to these functions via their fully qualified names (e.g. C<Carp::carp>).
This is ultimately annoying and repetitive.
This module populates the C<_> package (a nod to JavaScript's Underscore.js library) with various helpers so that they can be used without having to import them, with a per-usage overhead of only three characters C<_::>.
The large number of dependencies makes this module somewhat heavyweight, but it avoids the “is C<any> in List::Util or List::MoreUtils”-problem.
In retrospect, choosing the C<_> package name was a mistake:
A certain part of Perl's infrastructure doesn't recognize C<_> as a valid package name (although Perl itself does).
More importantly, Perl's filetest operators can use the magic C<_> filehandle which would interfere with this module if it were intended for anything else than fully qualified access to its functions.
Still, a single underscore is less intrusive than some jumbled letters like C<Ut::any>.
=head4 Scope and Function Naming
This module collects various utility functions that – in my humble opinion – should be part of the Perl language, if the main namespace wouldn't become too crowded as a result.
Because everything is safely hedged into the C<_> namespace, we can go wild without fearing name collisions.
However, a few naming conventions were adhered to:
=over 4
=item *
Functions with a boolean return value start with C<is_>.
=item *
If the source module already provided a sensible name, it is kept to reduce confusion.
=item *
Factory functions that return an object use CamelCase.
=back
=head1 RELATED MODULES
The following modules were once considered for inclusion or were otherwise influental in the design of this collection:
L<Data::Types|Data::Types>,
L<Data::Util|Data::Util>,
L<Params::Util|Params::Util>,
L<Safe::Isa|Safe::Isa>.
=head1 BUGS
Please report any bugs or feature requests on the bugtracker website
When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.
=head1 AUTHOR
Lukas Atkinson <amon@cpan.org>
=head2 CONTRIBUTORS
=over 4
=item *
Lukas Atkinson (cpan: AMON) <amon@cpan.org>
=item *
Olivier Mengué (cpan: DOLMEN) <dolmen@cpan.org>
=back
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2014 by Lukas Atkinson.
This is free software, licensed under:
The GNU General Public License, Version 3, June 2007
=cut