perldoc -v $!:
When referenced, $! retrieves the current value of the C "errno"
integer variable. If $! is assigned a numerical value, that value
is stored in "errno". When referenced as a string, $! yields the
system error string corresponding to "errno".
... Assignment to $! is similarly ephemeral. It
can be used immediately before invoking the "die()" operator, to
set the exit value, or to inspect the system error string
corresponding to error n, or to restore $! to a meaningful state. (emphases mine)
That explains why you can't do the obvious:
#!/usr/bin/env perl
say_hi() or die "ERROR: sub say_hi had an issue: Details => $!\n";
sub say_hi{
my ($name) = @_;
unless ($name) {
$! = 'no name';
return;
}
print "Hi $name\n";
return 1;
}
That results in:
ERROR: sub say_hi had an issue: Details =>
Consider using eval/$@:
#!/usr/bin/env perl
eval { say_hi() } or die "ERROR: sub say_hi had an issue: Details => $@\n";
sub say_hi{
my ($name) = @_;
$name or die "no name\n";
print "Hi $name\n";
return 1;
}
See also "Is Try::Tiny still recommended for exception handling in Perl 5.14 or later?"
If you follow brian's recommendation in this answer to that question, you can do something like:
#!/usr/bin/env perl
use strict;
use warnings;
my $r = say_hi();
$r->{success}
or die sprintf "ERROR: sub say_hi had an issue: Details => %s\n", $r->{reason};
sub say_hi{
my ($name) = @_;
$name or return { success => 0, reason => 'Missing "name"' };
print "Hi $name\n";
return { success => 1 };
}
Or, you can use Params::Validate or Validate::Tiny etc.