Input/Output The Definitive Guide

Correctly use Perl 6 IO

The Wrong Way To Do Things

This section describes how NOT to do Perl 6 IO.

Leave $*SPEC Alone

You may have heard of $*SPEC and seen some code or books show its usage for splitting and joining path fragments. Some of the routine names it provides may even look familiar to what you've used in other languages.

However, unless you're writing your own IO framework, you don't ever need to use $*SPEC! $*SPEC provides low-level stuff and its use will not only make your code horrible to read, you'll likely introduce security issues (e.g. null characters)!

The IO::Path type is the workhorse of Perl 6 world. It caters to all the path manipulation needs as well as provides shortcut routines that let you avoid dealing with file handles. Use that instead of the $*SPEC stuff.

Tip: you can join path parts with / and feed them to IO::Path's routines; they'll still do The Right Thing™ with them, regardless of the operating system.

# WRONG!! TOO MUCH WORK! 
my $fh = open $*SPEC.catpath: '''foo/bar'$file;
my $data = $fh.slurp;
$fh.close;

# RIGHT! Use IO::Path to do all the dirty work my $data = 'foo/bar'.IO.add($file).slurp;

Stringifying IO::Path

Don't use .Str method to stringify IO::Path objects, unless you just want to display them somewhere for information purposes or something. The .Str method returns whatever the basic path string the IO::Path was instantiated with. It doesn't consider the value of $.CWD attribute. For example, this code is broken:

# WRONG!! .Str DOES NOT USE $.CWD! 
my $path = 'foo'.IO;
chdir 'bar';
run <tar -cvvf archive.tar>~$path;

The chdir call changed the value of the current directory, but the $path we created is relative to the directory before that change.

However, the IO::Path object does know what directory it's relative to. We just need to use .absolute or .relative to stringify the object. Both routines return a Str object; they only differ in whether the result is an absolute or relative path. So, we can fix our code like this:

# RIGHT!! .absolute does consider the value of $.CWD! 
my $path = 'foo'.IO;
chdir 'bar';
run <tar -cvvf archive.tar>$path.absolute;
# Also good: 
run <tar -cvvf archive.tar>$path.relative;

Be mindful of $*CWD

While usually out of view, every IO::Path object, by default, uses the current value of $*CWD to set its $.CWD attribute. This means there are two things to pay attention to.

temp the $*CWD

This code is a mistake:

# WRONG!! 
my $*CWD = "foo".IO;

The my $*CWD made $*CWD undefined. The .IO coercer then goes ahead and sets the $.CWD attribute of the path its creating to the stringified version of the undefined $*CWD; an empty string.

The correct way to perform this operation is use temp instead of my. It'll localize the effect of changes to $*CWD, just like my would, but it won't make it undefined, so the .IO coercer will still get the correct old value:

temp $*CWD = "foo".IO;

Better yet, if you want to perform some code in localized $*CWD, use the indir routine for that purpose.