when(), Try::Tiny, and autodie

I’m working on Chapter 17, which is the catch-all chapter for topics we think that segue into the other books in the Learning Perl series. Although Mastering Perl has an entire chapter on catching and reporting errors, we want to at least survey the topic in Learning Perl.

The first edition of Learning Perl noted that eval existed and gave a couple of examples, and in each subsequent edition the discussion became more involved.

Starting with the fourth edition, we devoted a chapter to using Perl modules, acknowledging the fact that Perl’s greatest feature is CPAN. In that edition, it was fairly late in the book. In the fifth edition, we moved that chapter toward the middle of the book. In each case, this means that we can then use Perl modules, whether from the Standard Library or CPAN, for the rest of the book since we’ve covered the idea of using modules. Our goal is always to cover any topic before we use it.

Since the sixth edition also covers modules, we can use it when we talk about catching errors. That means that we can talk about autodie, the pragma that became part of the Perl core in 5.10.1, and Try::Tiny, which is not a core module. We might have covered autodie in the fifth edition, but we only covered up to Perl 5.10.0. Paul Fenwick just barely missed the cut-off.

So, while working on the eval section, I was playing with some examples. I covered eval, Try::Tiny, and autodie separately, but I was wondering what would happen if I combined them. Could try and autodie cooperate?

I started with the example from the autodie documentation:

eval {
   use autodie;
   open(my $fh, '<', $some_file);
   my @records = <$fh>;
   # Do things with @records...
   close($fh);
};

given ($@) {
   when (undef)   { say "No error";                    }
   when ('open')  { say "Error from open";             }
   when (':io')   { say "Non-open, IO error.";         }
   when (':all')  { say "All other autodie errors."    }
   default        { say "Not an autodie error at all." }
}

Then I added Try::Tiny and started playing around with it:

use 5.010;

use autodie;
use Try::Tiny;

my $filename = '/does/not/exist';
try {
  open my $fh, '>', $filename; # still dies on error
  }
catch {
  when( 'open'  ) { say 'Got an open error'; continue; }
  when( 'close' ) { say 'Got an open error'; continue; }
  when( ':io'   ) { say 'Got an io error';   continue; }
  };

The output is not what I expected. It does what it looks like it should do:

Got an open error
Got an io error

I was surprised that this worked, and that it worked without a warning. That when is outside an official topicalizer (something that sets $_, such as given or foreach ).

That’s one of the interesting parts of writing a book. To properly research something, we think about the different ways we might combine things and especially how things might break. When we’re teaching, we’re going to run into all sorts of crazy syntheses of the topics. With a bit of experience, we can anticipate some of that, and when we do we discover some interesting things like this.

3 thoughts on “when(), Try::Tiny, and autodie”

  1. If you’re going to do this, shouldn’t you localize $@ yourself? I’ve chased some nasty bugs the stemmed from un-localized special variables.

    1. We do mention local()-ing $@ in the text, but I was just playing with this small snippet to see if the three pieces played together. Most of the examples are not full programs, otherwise instead of one line to highlight only the part we are talking about:

      my $barney  eval { $fred / $dino };
      

      we’d end up with this or worse for every example:

      use 5.010;
      use utf8;
      use strict;
      use warnings;
      
      my $barney = do {
          local $@;
          my $quotient = eval { $fred / $dino };
          if( $@ ) { ... } else { $quotient }
          }
      

      The trick with a tutorial book is to show enough to let the reader understand the point we are making with distracting him with other things.

      In Learning Perl, we’re really teaching basic Perl syntax and features rather than Perl programming. People have to start somewhere and they have to have some sort of limit on what they need to do to progress so they can accomplish stuff. Learning Perl gets them started, and when they aren’t afraid of the things they learned in that book, they should keep going to refine their knowledge.

      You’ll see the difference in how we treat subjects in The Effective Perler. There we assume that the reader knows Perl and we can get deep into the woods on many subjects.

  2. That’s intentionally supported (and I think documented too, hopefully). It uses ‘for’ as a topicalizer so it’s localized (not given ebecause lexical $_ won’t propagate to the catch {}’s lexical scope).

    However, this also means that lexical $_ can’t be used in the lexical scope of catch { }, because that protects $_ from being fiddled with in the dynamic scope.

Comments are closed.