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.

Updates to Chapter 16, “Process Management”

[This post notes differences between the fifth and sixth editions.]

“Process Management” has been one of our neglected chapters for updates, being much the same in the fifth edition as it was in the third. It’s at the end of the book and it doesn’t immediately assert itself as a chapter needing work like a chapter on regular expressions does. We don’t often think about new features in interprocess communication, at least until now.

We didn’t really have to change anything big, but there are a lot of additional examples for signals and piped opens. A lot has happened in those areas since we last added to this chapter.

In the fifth edition, we had a lot of words to warn about “unsafe signals”. Way back in Perl 5.7.3 (yes, before the fourth edition!), Perl moved to deferred signals. And, although the fourth edition covers Perl 5.8, that really means that a lot of people were still using Perl 5.005 so we still needed the warning. I bet most people had moved to Perl 5.6 by the time the fifth edition came out. But, now that it’s 2011, we’re going to assume that life started with Perl 5.8. If people are using something before then, they are out of luck with the new edition.

You can read about signals in the perlipc documentation. Instead of interrupting the program, which might be in the middle of a system call, allocating memory, or something else that needs to finish so things don’t go horribly wrong, perl lets them finish. For most signals, perl merely sets a flag and keeps doing what it was doing. When perl gets to a safe spot, like the space between opcodes, it checks for the signal flags and handles what it finds.

For the pipes, it’s time to move those to the three (or more) argument form of open. Previously we had this example:

open DATE, "date|" or die ...;

With the three-argument open:

open my $date_fh, '-|', 'date' or die ...;

Note that autodie doesn’t show up until the next chapter, so we still use die.

And, since we emphasize modules much more in this edition, we added a short introduction to IPC::System::Simple, even though it’s not a core module (yet).

Basic books sell better

When I posted my Perl plans for 2011, Robert asked me why Mastering Perl might not have another print edition. It’s mostly due to the sales for advanced books. I don’t think this is really about Perl or the topic involved. Take any topic and specialize it more and more. You cut down on your audience each time because fewer and fewer people want to follow you along. Most people get by just fine with the basics in Learning Perl.

Normalized print book sales of the my Perl books

All the numbers are normalized to the Learning Perl sales in the last quarter of 2009, and the Y axis is logarithmic.

Most authors would like to have the sales of Intermediate Perl. We’re fortunate to have the sales of Learning Perl that let it go on to a fifth edition.

Updates to chapter 17, “Advanced Topics”

[This post notes differences between the fifth and sixth editions.]

Chapter 17 of Learning Perl is a catch-all chapter at the end of the book. We cover some “advanced” items to pique the interest of the reader and to segue into Intermediate Perl. Mostly, this covers grep, map, and eval.

Since we also cover Perl modules, we expanded this chapter a bit. The list operators are nice, and since list operations are most of many programs, so we should at least mention List::Util and List::MoreUtils.

The eval is the minimum for dealing with errors, and it also provides quite a bit of trouble as it handles $@. So, we also introduce Try::Tiny. We don’t say much about it, but anyone who makes it this far at least finds out that it exists. While we are at it, we might as well throw in autodie, especially since it’s part of the Standard Library.

Updates to Chapter 15, “Smart matching and given-when”

[This post notes differences between the fifth and sixth editions.]

We published Learning Perl, 5th Edition in 2008, meaning that we were writing it toward the end of 2007. Perl 5.10 had just come out after being years in development. We just had a peek at the final features as the book was going to press, so the 5th Edition covers Perl 5.10.0. That point release is important, because Perl 5.10.1 changed the smart match in one significant way; it’s now not commutative. We covered smart matching and the new given-when in Chapter 15, which we need to slightly update.

As people started to use Perl 5.10, they realized there were many problems with the new smart match operator, ~~. The smart match looks at its operands and then does something special based an what it sees. It doesn’t have to do the same sort of task for every set of operands. For instance,

$fred ~~ $barney;  # $fred eq $barney

In Perl 5.10.0, perl didn’t care which side the operands were on. The smart match was supposed to be commutative, meaning that $n ~~ $m should be the same as $m ~~ $n, where the operands are flipped around. That doesn’t quite work out because the smart match is a little too dumb about strings and numbers, so these were the same thing because the order of the operands doesn’t matter:

if( 1 ~~ '1one' ) { ... }
if( '1one' ~~ 1 ) { ... }

Both of those are false in Perl 5.10.0 because each does a string comparison, even though one of them is a number that perl must convert to a string. That means that you couldn’t use a smart match if you wanted a numeric comparison. This is important because the when in uses $_ as the lefthand side of the implicit smart match and you specify the righthand operand:

given( $number ) {
    when( 4 ) { ... }
    }

Since you put a number there, you’d think that you’d get a numeric comparison, but you don’t. That’s a problem. Since Perl 5.10.1, the order of the operands matter, and you basically have to consult the table in perlsyn‘s section on the “Switch statement”.

This change doesn’t affect too much of the text in Chapter 15 because we shied away from some of the more complex examples. Our code still works, and we hope that people will skip over Perl 5.10 to at least Perl 5.12, but we never know. We’ll still have to add some warnings about that particular release of Perl.