Hit a bug at work a few days ago; it looked as if
Try::Tiny was not doing its job of catching an exception, letting it pass right through instead. The exception was thrown several layers below the
try, which itself was also several layers deep, with lots of
evals in-between. It took me quite a while to prune away all the unrelated bits, until the problematic part of the code was pared down to its simplest expression:
try { die } catch { warn };
Huh? This piece of code should catch the
die and emit a warning instead, but here it was, dying on me. What was going on?
Actually, there's nothing wrong with that code, and it will do exactly what it is meant to do.
Unless you forget the use
Try::Tiny. Then all hell breaks loose.
In most cases, failing to use a module will typically result in a "Undefined subroutine" or "Can't locate object method" error message that will point to your mistake in an obvious manner. But in this case, the manifestation can be quite puzzling.
At first, I figured that maybe perl was parsing the first block (i.e. the first argument of the
try subroutine) as an anonymous hash; since the
try prototype hasn't been declared yet, there's no way to know that this argument is actually a block. The
die will then be executed during the evaluation of the
try arguments.
Close, but no cigar. Turns out that perl is indeed parsing it as a block -- just not in the context we expect:
$ perl -MO=Deparse -e 'try { die } catch { warn }'
do {
die
}->try(do {
warn
}->catch);
Here's the indirect object syntax rearing it ugly head; Perl assumes that
try is actually a method to be called on whatever class/object the following block will return. Yuck. (Notice that the same also applies to
catch.)
(Maybe this will finally push me to break the habit of using that syntax for class methods. Yeah, I know it's unhealthy, but I can't resist...)