Skip to content

Commit 6ca67e4

Browse files
committed
[io grant] Start sketching out Definitive IO Guide™
Likely will replace the current io.pod6 eventually. Start it off in a separate file for now.
1 parent bc0c585 commit 6ca67e4

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

doc/Language/io-guide.pod6

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
=begin pod :tag<perl6>
2+
3+
=TITLE Input/Output The Definitive Guide
4+
5+
=SUBTITLE The dos and don'ts of Perl 6 IO
6+
7+
8+
=head1 The Wrong Way To Do Things
9+
10+
This section describes how NOT to do Perl 6 IO.
11+
12+
=head2 Leave $*SPEC Alone
13+
14+
You may have heard of L«C<$*SPEC>|/language/variables#Dynamic_variables» and
15+
seen some code or books show its usage for splitting and joining path fragments.
16+
Some of the routine names it provides may even look familiar to what you've
17+
used in other languages.
18+
19+
However, unless you're writing your own IO framework,
20+
you don't ever need to use L«C<$*SPEC>|/language/variables#Dynamic_variables»!
21+
L«C<$*SPEC>|/language/variables#Dynamic_variables» provides low-level stuff and
22+
its use will not only make your code horrible to read, you'll likely introduce
23+
security issues (e.g. nul bytes)!
24+
25+
The L«C<IO::Path>|/type/IO::Path» type is the workhorse of Perl 6 worl. It
26+
caters to all the path manipulation needs as well as provides shortcut routines
27+
that let you avoid dealing with file handles. Use that instead of the
28+
L«C<$*SPEC>|/language/variables#Dynamic_variables» stuff.
29+
30+
Tip: you can join path parts with C</> and feed them to
31+
L«C<IO::Path>|/type/IO::Path»'s routines; they'll still do The Right Thing™ with
32+
them, regardless of the operating system.
33+
34+
=for code :skip-test
35+
# WRONG!! TOO MUCH WORK!
36+
my $fh = open $*SPEC.catpath: '', 'foo/bar', $file;
37+
my $data = $fh.slurp;
38+
$fh.close;
39+
40+
# RIGHT! Use IO::Path to do all the dirty work
41+
my $data = 'foo/bar'.IO.add($file).slurp;
42+
43+
=head2 Stringifying IO::Path
44+
45+
Don't use C<.Str> method to stringify L«C<IO::Path>|/type/IO::Path» objects,
46+
unless you just want to display them somewhere for information purposes or
47+
something. The C<.Str> method returns whatever the basic path string the
48+
L«C<IO::Path>|/type/IO::Path» was instantiated with. It doesn't consider the
49+
value of L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD». For example, this
50+
code is broken:
51+
52+
=for code :skip-test
53+
# WRONG!! .Str DOES NOT USE $.CWD!
54+
my $path = 'foo'.IO;
55+
chdir 'bar';
56+
run <tar -cvvf archive.tar>, ~$path;
57+
58+
The L«C<chdir>|/routine/chdir» call changed the value of the current directory,
59+
but the C<$path> we created is relative to the directory before that change.
60+
61+
However, the L«C<IO::Path>|/type/IO::Path» object I<does> know what directory
62+
it's relative to. We just need to use L«C<.absolute>|/routine/absolute» or
63+
L«C<.relative>|/routine/relative» to stringify the object. Both routines return
64+
a L«C<Str>|/type/Str» object; they only differ in whether the result is an
65+
absolute or relative path. So, we can fix our code like this:
66+
67+
=for code :skip-test
68+
# RIGHT!! .absolute does consider the value of $.CWD!
69+
my $path = 'foo'.IO;
70+
chdir 'bar';
71+
run <tar -cvvf archive.tar>, $path.absolute;
72+
# Also good:
73+
run <tar -cvvf archive.tar>, $path.relative;
74+
75+
=head2 Be mindful of $*CWD
76+
77+
While usually out of view, every L«C<IO::Path>|/type/IO::Path» object, by
78+
default, uses the current value of
79+
L«C<$*CWD>|/language/variables#Dynamic_variables» to set its
80+
L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD». This means there are two
81+
things to pay attention to.
82+
83+
=head3 temp the $*CWD
84+
85+
This code is a mistake:
86+
87+
=for code :skip-test
88+
# WRONG!!
89+
my $*CWD = "foo".IO;
90+
91+
The C<my $*CWD> made L«C<$*CWD>|/language/variables#Dynamic_variables»
92+
undefined. The L«C<.IO>|/routine/IO» coercer
93+
then goes ahead and sets the L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD»
94+
of the path its creating to the stringified version of the undefined C<$*CWD>;
95+
an empty string.
96+
97+
The correct way to perform this operation is use
98+
L«C<temp>|/routine/temp» instead of C<my>. It'll localize the effect of changes
99+
to L«C<$*CWD>|/language/variables#Dynamic_variables», just like C<my> would,
100+
but it won't make it undefined, so the L«C<.IO>|/routine/IO» coercer will still
101+
get the correct old value:
102+
103+
=for code :skip-test
104+
temp $*CWD = "foo".IO;
105+
106+
Better yet, if you want to perform some code in localized
107+
L«C<$*CWD>|/language/variables#Dynamic_variables», use the
108+
L«C<indir> routine|/routine/indir» for that purpose.
109+
110+
=end pod
111+
112+
# vim: expandtab shiftwidth=4 ft=perl6

0 commit comments

Comments
 (0)