Clean::Eval - Run code under eval without leaking $@ and get a rich
error object back on failure.
Perl's built-in eval is the standard way to trap exceptions, but it has two
long-standing ergonomic problems:
- It modifies the global
$@, which can be clobbered by destructors or other code running during stack unwind, leading to lost or corrupted error messages. - The return value of
evalcan be ambiguous: a successful eval that legitimately returns a false value is indistinguishable from a failure unless you check$@.
Clean::Eval wraps eval in a way that avoids both problems. It localizes
$@ so the caller's copy is never touched and always returns a blessed
result object that is overloaded for boolean and string context. The object
is true on success and false on failure regardless of what the wrapped code
returned, so a single if check is enough to distinguish the two.
On success the block form also stashes the block's return value (taken in
scalar context) in an out field on the object, so the typical pattern is:
if (my $ev = clean_eval { get_message() }) {
$msg = $ev->out;
}
else {
die $ev; # stringifies to the trapped error
}
Both a block form (clean_eval { ... }) and a string form
(clean_string_eval $code) are provided. The string form rewrites #line
information so that any error reports the file and line of the caller, not an
anonymous (eval N). The string form does not capture a return value -
see "clean_string_eval".
use Clean::Eval qw/clean_eval clean_string_eval last_error/;
# Block form - object is always returned; bool overload picks
# success vs failure; on success ->out holds the block's scalar
# return value.
my $msg;
if (my $ev = clean_eval { get_message() }) {
$msg = $ev->out;
}
else {
die $ev; # stringifies to trapped error
}
# Or, branchless:
my $ev = clean_eval { risky() };
die "Failed: $ev\n at $ev->{file} line $ev->{line}\n" unless $ev;
my $result = $ev->out;
# String form - same overloaded object, but no ->out is ever set
# (see "clean_string_eval" below). No need to add a trailing "; 1"
# - it is appended for you.
my $ev = clean_string_eval 'use SomeOptionalModule';
warn "Optional dep missing: $ev\n" unless $ev;
# Retrieve the most recent failure from anywhere
my $last = last_error();
Nothing is exported by default. The three functions below may be imported individually using Importer-style syntax:
use Clean::Eval qw/clean_eval clean_string_eval last_error/;
-
$ev = clean_eval { BLOCK }
Run
BLOCKundereval. Always returns aClean::Evalresult object (see "RESULT OBJECT").$@in the caller's scope is not touched.On success the block's return value is captured in scalar context and stored in the object's
outfield. Scalar context is forced because the object is a scalar-context-only carrier - capturing a list there would require API choices (arrayref? flatten?) that would surprise callers. If you need list-context results, assign to an outermy @listfrom inside the block:my @rows; my $ev = clean_eval { @rows = fetch_rows() }; die $ev unless $ev;On failure the
outkey is not present on the object and theerror,package,file, andlinekeys are populated instead.The prototype is
(&), so the block form works without a leadingsub. -
$ev = clean_string_eval $STRING
Run
$STRINGas Perl code undereval. Always returns aClean::Evalresult object.$@in the caller's scope is not touched.Unlike the block form, the string form never captures a return value. The
outfield is always absent on the result object, even on success. This is because the body of a string eval is not necessarily a value- producing expression: it might be defining a subroutine, opening aBEGIN/INIT/ENDblock, declaring a package, loading a module viause, or otherwise producing something that has no meaningful "scalar return value" to record. Trying to capture and stash a result in those cases would just be misleading. If you need a value out of string eval'd code, write to an outerourpackage variable from inside the string, or useclean_eval { eval $string }and capture the result yourself.A
#linedirective is prepended to$STRINGusing the caller's filename and line number, so any error or warning produced by the eval'd code refers to the source location of theclean_string_evalcall rather than to an anonymous eval string.A trailing
; 1is also appended to$STRING, so you do not need to remember the usualeval "...; 1"success guard - success is recorded on the result object regardless of what the final statement in$STRINGevaluates to. Including the; 1yourself is harmless.The prototype is
($), so a single scalar argument is taken. -
$err = last_error()
Return the result object of the most recent failure produced by
clean_evalorclean_string_evalanywhere in the program, orundefif no failure has been recorded yet. Successful calls do not reset this slot. Useful for code paths that discarded the result object or want to inspect a previous failure after the fact.Caveat:
last_erroris a global slot and is subject to the same class of bug that makes raw$@fragile. If aDESTROYmethod (or anything else running during stack unwind) callsclean_evalorclean_string_evaland that inner call fails, it will overwrite the global and the error you actually cared about will be lost.last_erroris a convenience, not a guarantee - the only robust way to inspect a particular failure is to capture the result object ofclean_eval/clean_string_evaldirectly at the call site and keep it in a lexical of your own.
Both clean_eval and clean_string_eval always return a blessed hashref
of class Clean::Eval. It overloads boolean and stringification context:
- Boolean context: true on success, false on failure. This is computed from
the
okfield, so a successful eval whose block legitimately returned a false value is still distinguishable from a failure. - String context: the trapped error message on failure (the value
$@had inside the eval), or the empty string on success. This lets you writedie $evon a failure without having to dig out a field.
The object is a plain hashref with the following keys. Which keys are present depends on whether the eval succeeded:
-
ok
Always present.
1on success,0on failure. -
out
Present only on success, and only for the block form. Holds the block's return value, taken in scalar context. Absent (the key does not
existsat all) on failure, and absent forclean_string_evalin all cases. -
error
Present only on failure. The trapped error message (string or object, whatever was
die'd). -
package
The package the call was made from. Always present.
-
file
The file the call was made from. Always present.
-
line
The line the call was made from. Always present.
Convenience accessors ok, out, and error return the corresponding
fields. to_string returns the same string the "" overload yields.
You can, but you have to be careful. The idiomatic safe pattern looks like:
my $ok = eval { ...; 1 };
if (!$ok) {
my $err = $@;
...
}
This is correct but verbose, and the ; 1 trailer is easy to forget. The
$@ variable is also famously fragile: destructors that run during stack
unwind can call eval themselves and reset it before you read it. Localizing
$@ the way Clean::Eval does avoids that class of bug entirely.
This looks natural but contains a subtle bug. A lexical introduced by
my is not in scope until the statement that declared it has
finished, so the $ev referenced by die "$ev" is a different,
package-global $ev (which is undef):
use Clean::Eval qw/clean_eval/;
# WRONG - $ev inside the die is the package global, not the lexical;
# the die fires (clean_eval returned a false-overloaded object) but
# with an empty message.
my $ev = clean_eval { die "foo" } or die "$ev";
Declare the lexical on its own statement first so it is in scope by the
time the or die runs:
use Clean::Eval qw/clean_eval/;
# CORRECT - $ev refers to the lexical in both spots
my $ev;
$ev = clean_eval { die "foo" } or die "$ev";
Or split the check off into its own statement, which has the same effect:
use Clean::Eval qw/clean_eval/;
my $ev = clean_eval { die "foo" };
die "$ev" unless $ev;
The reliable rule: do not reference a my-declared variable in the same
statement that declares it. Running with use warnings will diagnose
this with "Name main::ev used only once: possible typo".
clean_eval stashes the block's return value in $ev->out, but
it does so in scalar context. A block that returns a list will be
collapsed to the last element (or to the list count, depending on the
expression):
use Clean::Eval qw/clean_eval/;
my $ev = clean_eval { (1, 2, 3) };
# $ev->out is 3, not [1, 2, 3]
If you need a list result, write to an outer lexical from inside the block:
my @rows;
my $ev = clean_eval { @rows = fetch_rows() };
die $ev unless $ev;
# use @rows here
Scalar context is forced deliberately - the out field is a single
scalar slot, and silently picking a list-handling convention would
surprise callers.
The string form never records a return value, even on success. A string
eval may be defining subs, opening BEGIN/END blocks, loading
modules, or otherwise doing things with no meaningful scalar result. If
you need a value back from a string eval, write to an our package
variable from inside the string, or wrap a real eval $str inside a
clean_eval block and capture from there.
With a raw eval $string, the eval'd code can see any my variables
in the surrounding scope. clean_string_eval cannot: the string is
eval'd inside this module, so the caller's lexicals are out of reach.
Only package globals are visible.
use Clean::Eval qw/clean_string_eval/;
my $x = 42;
my $ret = clean_string_eval 'print $x';
# $ret is an error: "Global symbol $x requires explicit package name"
# (or, without strict, $x is just an unrelated undef global)
our $y = 42;
clean_string_eval 'print $y'; # prints 42 - $y is a package global
If you need to feed values in, pass them through globals you control or
through the environment, or build a closure and use clean_eval with a
block instead.
The block passed to clean_eval is an anonymous subroutine. A return
inside it returns from that anonymous subroutine - not from the
enclosing named sub - and clean_eval still gets control back and
returns 1 for success.
sub do_work {
my $ok = clean_eval {
return if $skip; # returns from the block only
risky_thing();
};
return 0 unless $ok;
...
}
This matches the behavior of plain eval { ... }.
The (&) prototype makes clean_eval parse a literal block; it will
not accept a coderef in scalar variable form:
my $cref = sub { die "foo" };
clean_eval $cref; # syntax error / wrong parse
Workarounds:
clean_eval(\&named_sub); # named sub via \&
clean_eval { $cref->() }; # wrap in a literal block
&Clean::Eval::clean_eval($cref); # bypass the prototype
Try::Tiny, Syntax::Keyword::Try, Feature::Compat::Try.
The source code repository for Clean-Eval can be found at
https://github.com/exodist/Clean-Eval/.
- Chad Granum exodist@cpan.org
- Chad Granum exodist@cpan.org
Copyright 2026 Chad Granum exodist7@gmail.com.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See http://dev.perl.org/licenses/