Patching the Perl core for lazy file handle objects

Reading time: 2 minutes

Last night I was inspired to patch the Perl core to load IO::File on demand when calling a method on a file handle would otherwise die. This effectively makes file handles actual “lazy” IO::File objects. If you want to use file handles procedurally, you never need IO::File. But if you try to treat handles as objects and call methods on them, IO::File gets loaded when you need it.

Here’s how it works. As of Perl 5.12, internally, Perl 5 file handles are represented as IO::File objects:

$ perl -E 'say(ref(*STDERR{IO}))'
IO::File
$ perl -E 'open my $fh, ">", "/dev/null"; say(ref(*{$fh}{IO}))'
IO::File
$ perl -E 'my $str; open my $fh, ">", \$str ; say(ref(*{$fh}{IO}))'
IO::File

However, trying to call methods on them fails if you haven’t loaded IO::File (or a parent class like IO::Handle):

$ perl -E 'STDOUT->binmode(":utf8"); say "\x{263a}"'
Can't locate object method "binmode" via package "IO::File" at -e line 1.

With the patch I wrote, just before such a method call would die, if the call is on an IO::File object and $INC{'IO/File.pm'} doesn’t exist, Perl attempts to load IO::File and tries to resolve the method again:

$ bleadperl -E 'STDOUT->binmode(":utf8"); say "\x{263a}"'
☺

This has zero performance impact on code that doesn’t call methods on file handles or that loads IO::File (or a parent class) before calling a method on a file handle. It only kicks in when Perl would otherwise die on you.

Barring any issues emerging, this feature will be available in the development release of Perl 5.13.8 on or around December 20, just in time for Christmas.

•      •      •

If you enjoyed this or have feedback, please let me know by or