In Perl, exceptions are a well known and widely used mechanism. It is an old
feature that has been enhanced over time. At the basic level, exceptions are
triggered by the keyword
die. Exceptions were initially used as a way to stop
the execution of a program in case of a fatal error. The too famous line:
is a good example.
The original way to catch exceptions in Perl has a somewhat strange syntax,
it’s based on the
eval keyword and the special variable
Nowadays, exceptions are usually thrown using
croak and friends, from the
Carp module. It allows for a much better flexibility about where the exception
seems to originate, and how to display the stack trace, if any.
Catching exceptions with
eval is also supersed by try/catch mechanisms. The
most used one is via the [
Try::Tiny][try-tiny] module by Yuval Kogman and Jesse Luehrs,
and goes like this:
The good thing about
croak), is that it’s very easy to use, when
given a string. It’s perfect for using in scripts, or moderately big
projects. However, for more features, or extensive usage of exceptions, then
it’s better to throw objects instead of strings, like this:
For this snippet of code to work, the
MyExceptions::IO::File class has to be
declared, its fields as well, and the it should probably inherit from
MyExceptions::IO. So it requires some amount of work.
Some modules have been created - long time ago - to automate or help with
declaring exception classes. The most well known one is [
Dave Rolsky. For instance, here is how to declare two exceptions matching with
And then, here is the code to make use of that and throw an exception when failing to open a file:
When using objects as exceptions, a set of features becomes available, thanks to Object Oriented Programming. Inheritance, attributes and introspection are some of them. However the most visible and used feature is about catching such exceptions:
As you can see, it’s easy to introspect an exception if it’s an object. In this
case we use the
isa keyword to know if the exception is or inherits from a
given class name.
As we saw in the previous chapter, Perl allows exceptions being whatever you like (string, objects, but actually numbers, structures, etc, work as well).
Usually, when starting a project, the author decides whether to use simple strings or objects with a class hierarchy. With very big projects, it is sometimes not possible to impose one kind of exceptions. This may be due to legacy code, a subproject that was included, or the wish to give people freedom about what they want to use depending on the context.
In these cases, the code may have to handle exceptions of two kinds: strings and objects. This can be done via this kind of code:
The previous code snippet suffers from increased complexity due to the additional checks and two different codepaths for handling potential errors. This is clearly both suboptimal and error prone.
Another issue is that some code may consider that the exception it is catching is of one type, whereas it could be of an other type, especially because of the action-at-distance nature of the exception. Consider this function:
This code assumes that the exception will always be an object. However,
let’s consider this: in following example, the function
do_stuff is called
(its original code is unchanged), but before doing so, the special signal
__DIE__ is changed.
The first line of the example is being called when an exception is raised, and
will be executed instead of propagating the exception. What this code does is
FATAL: to it, then propagate the exception again by using
Alas, it is doing so in a naive way, by forcing the exception (in
$_) to be
evaluated as a string. So when the exception is then re-thrown, it is now a
string ! and Boom, the
->isa call in
do_stuff won’t work.
The worst thing about this kind of issue is that it doesn’t appear at compile time, nor at execution time, but at exception time, which is the worst time…
So at that point, most developers will choose the following strategy. Use object exceptions for their code, but guard against receiving string exceptions, and also make their object exceptions nicely degrade into strings, by using stringification overloading. That means that if an object exception is managed by a handler that threats it as a string, the exception will transform itself into a string, and try to present some meaningful aspect of itself.
The issue is that handling exception is now back to square one, having to deal with strings, trying to parse it looking for meaningful information to hopefully make a good decision.
What if, instead of taking an object exception and downgrading it to a string while keeping as much information as possible, one starts from a string, and enhance it until it looks like an object, without being one ? That way we would have the best of both worlds
This is what
Exception::Stringy tries to achieve.
A perfect exception would have these features:
This set of features is not big, but it’s probably enough for a start. Let’s see how we can implement them in a simple string. We’re going to use an exception with these attributes:
Let’s start with the first feature: be a string, containing an error message. That’s easy:
Being an instance of a class is usually done in Perl by using
bless on a
ScalarRef. But we don’t want the eception to be an object. What
bless does -
and what it ultimately means to “be an instance of a class”, is just attaching
a label to a value. Let’s do that, by having a label as a substring in our
exception. For instance:
We could add a magic mark or have a more complex label syntax to make sure it’s a legit label.
To know what the class of a given exception is, we just need to extract the label, for instance with a regex.
Inheritance is easy, it only requires that standard Perl classes be created to map the exception labels, and then Perl usual inheritance can be used.
So, following our example, we need two packages,
@MyException::IO::ISA set to
['MyException']. This can
be made automatically at exception declaration time.
Exception::Stringy only handles simple field values, that is
strings and numbers basically. To put fields into our string, we need to be
able to identify them, so for instance with a separator between the different
fields, and an other one between a field name and its value. Like this:
And if the field name or value contains one of the separators (
]), let’s encode them in base64, and mark it as such.
So, by now, we have fleshed out a string with useful data, which is properly parseable, and can be described. Let’s add methods to the data now.
Given an exception, it is mandatory to be able to introspect and modify it, namely be able to:
In an ideal world, we would want methods, that we can call on our exception instances. However because our exceptions are regular strings, we can’t do this:
Usually, this way of calling a method (the arrow notation) works only if $exception is a blessed reference (that is, an object). However, there are other cases in which we can use the arrow notation, and have it work in a similar way. One of it is this one:
If $message is a variable that contains a reference on a subroutine, then the previous line will translate into:
And it works whatever the type of
$exception, like in our case, a string. So,
Exception::Stringy creates the needed subroutine references for the user and
allow such arrow notation, which is very similar to the OO method invocation. I
call these pseudo methods.
However, to avoid clobbering an existing variable, the pseudo methods need to
have names that are unlikely to be already used in the target package. It’s
even better if there is an option to add a prefix to these pseudo-methods.
Exception::Stringy provides these features. The default pseudo
method names are :
Finally, once we have created the exception, let’s throw it. The first think to
do is to implement a
throwor `raise class method on all the exception class,
so that we can do
That will basically craft a new exception string, with all the properties
encoded in it, and call
croak on it.
We can also use a pseudo method on an existing exception to (re)throw it:
Below is the synopsis of the
Exceptions::Stringy module. It’s basically a
wrap up of what has been explained above. The exceptions definition is heavily
This was an in-depth look at why and how to build up a resilient and non-intrusive exception mecanism. I hope to have demonstrated one aspect of the extreme flexibility of Perl.
Feel free to use
Exception::Stringy, it is being used in production code for
some time now. Feedback welcome !