NAME
Clean::Eval - Run code under eval without leaking $@ and get a rich error object back on failure.
DESCRIPTION
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, returns a true value on success, and returns an overloaded error object on failure. The error object stringifies to the trapped error and is always false in boolean context, so a single if check is enough to distinguish success from failure regardless of what the wrapped code returned.
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).
SYNOPSIS
use Clean::Eval qw/clean_eval clean_string_eval last_error/;
# Block form
if (my $ret = clean_eval { do_something_risky() }) {
# success path - $ret is a true value (1)
}
else {
# failure path - $ret is the error object
warn "Failed: $ret\n"; # stringifies to error message
warn " at $ret->{file} line $ret->{line}\n";
}
# String form (compiles at runtime). No need to add a trailing
# "; 1" - it is appended for you so the success path is unambiguous.
my $ret = clean_string_eval 'use SomeOptionalModule';
unless ($ret) {
warn "Optional dep missing: $ret\n";
}
# Retrieve the most recent error from anywhere
my $last = last_error();
EXPORTS
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/;
- $ret = clean_eval { BLOCK }
-
Run
BLOCKundereval. On success returns1. On failure returns aClean::Evalerror object (see "ERROR OBJECT").$@in the caller's scope is not touched.The prototype is
(&), so the block form works without a leadingsub. - $ret = clean_string_eval $STRING
-
Run
$STRINGas Perl code undereval. On success returns1. On failure returns aClean::Evalerror object.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 - the return value is unambiguously1on success 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 most recent error object produced by
clean_evalorclean_string_evalanywhere in the program, orundefif no error has been recorded yet. Useful for code paths that discarded the return value 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_eval, 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 return value ofclean_eval/clean_string_evaldirectly at the call site and keep it in a lexical of your own.
ERROR OBJECT
On failure both clean_eval and clean_string_eval return a blessed hashref of class Clean::Eval. It overloads stringification and boolean context:
Boolean context: always false. This lets
if (my $ret = clean_eval { ... })work without ambiguity even when the block legitimately returns a false value.String context: the trapped error (the value
$@had inside the eval).
The object is a plain hashref with the following keys:
- error
-
The trapped error message (string or object).
- package
-
The package the call was made from.
- file
-
The file the call was made from.
- line
-
The line the call was made from.
An additional to_string method is provided that returns the error message; it is equivalent to stringifying the object.
WHY NOT JUST USE eval?
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.
SEE ALSO
Try::Tiny, Syntax::Keyword::Try, Feature::Compat::Try.
SOURCE
The source code repository for Clean-Eval can be found at https://github.com/exodist/Clean-Eval/.
MAINTAINERS
AUTHORS
COPYRIGHT
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/