[% setvar title Replace default filehandle/select with $DEFOUT, $DEFERR, $DEFIN %]
Note: these documents may be out of date. Do not use as reference! |
To see what is currently happening visit http://www.perl6.org/
Replace default filehandle/select with $DEFOUT, $DEFERR, $DEFIN
Maintainer: Nathan Wiger <nate@wiger.org> Date: 17 Aug 2000 Last Modified: 13 Sep 2000 Mailing List: perl6-language-io@perl.org Number: 129 Version: 2 Status: Frozen
A basic Perl 5 implementation can be found as IO::Default at www.perl.com
The concept of a "default filehandle" is nebulous. Many users can't get their heads around it, and even if they can, it is still kind of mysterious.
This proposes that the "default filehandle" and 1-arg select
be dropped from Perl 6 and replaced with the variable $DEFOUT
,
the Default Output handle. Whatever this variable contains will
be what is printed to by default. This will make default output
consistent with the fileobject
approach and also other Perl special
variables.
In addition, this RFC proposes the addition of two new variables,
$DEFERR
and $DEFIN
, the Default Error and Default Input handles.
These can be used to redirect default error output and default input,
for added flexibility.
Some discussion, particularly by Jon Ericson, was recorded on this RFC.
Most of it boiled down to clarifications of issues. Jon questioned the
usefulness of $DEFERR
, but I think it serves a very useful purpose
since it allows you to redirect warnings (which would be directed to
$DEFERR
) without affecting statements that print to $STDERR
explicitly. There is also very nice symmetry between the DEF*
default
handles and the STD*
standard handles.
Currently, the "default filehandle" ("DF" to save typing) is
hidden. It can only be accessed or changed via the select
function.
With the advent of full-featured fileobjects, having a
special DF is unnecessary. Instead, we simply make a variable,
called $DEFOUT
, which is where unspecified output is sent.
Changing the DF is done through simple variable assignment.
Below are some examples of Perl 5 versus Perl 6 syntax:
Perl 5 Perl 6 ----------------------- -------------------------- select FILE; $DEFOUT = $FILE; $oldh = select; $oldh = $DEFOUT; $oldh = select FILE; $oldh = $DEFOUT; $DEFOUT = $FILE; $| = 1 $DEFOUT->autoflush(1); # or: autoflush $DEFOUT; # ?
What advantages besides a new syntax are there? Several:
1. The rule is easier. No DF, just "everything goes to $DEFOUT by default". Changing this is done by simple assignment. 2. It's less hidden. Most everything else in Perl is handled in variables such as @INC, %ENV, and so forth. This makes filehandles that much easier. 3. Access to object methods is available directly on the current filehandle object via $DEFOUT->method, eliminating the need for $|, $/, $\, and so on.[1] 4. No more 1-arg select vs. 4-arg select confusion.
Whatever object $DEFOUT
contains is what default output
goes to and what the object methods work on. There is no
mysterious hidden DF. $DEFOUT
works just like other
variables.
The new $DEFERR
variable can be used to easily redirect
error output as well, something you often want to do if
you're not attached to a tty. However, having a script that
runs easily in both environments is tricky in Perl 5:
if ( $have_a_tty ) { print STDERR "Warning: Bad stuff\n"; } else { print ERRORLOG "Warning: Bad stuff\n"; }
You have to put checks similar to this for many functions
and different situations. Plus, it doesn't catch carp
or
die
calls embedded in modules, meaning that you will
always lose some important error output.
With $DEFERR
, all error functions are set to act on it
instead of $STDERR
by default. So, the Perl 6 version
of the above is replaced by a single simple line of code
at the top of your main program:
$DEFERR = $ERRORLOG unless $have_a_tty;
Presto, all error-related functions now redirect to your error log automatically. All through simple assignment.
As Jon Ericson pointed out, this can be accomplished in a similar vein currently by reopening STDERR onto another file, or by using typeglob aliases to other open filehandles. However, it does not allow you to mix calls to files and STDERR like you can with files and STDOUT.
Using $DEFERR allows you to do so if you have functions that you want to
explicitly print to $STDERR
:
warn "Badness!"; # goes to $DEFERR print $STDERR "Badness!" # explicitly to $STDERR
This is not easy to do in Perl 5.[2] This is quite useful if, for example, you want a cron job to log all error either to the screen or log files but always print out the same message at the end:
$DEFERR = $ERRORLOG unless $have_a_tty; # ... warn "Bad stuff: $!"; # to $DEFERR # ... carp "This too: $!"; # ditto # ... if ( $really_bad ) { print $STDERR "Fatal error! Check the logfiles!\n"; exit 1; }
That way you'd get a cron-generated message (perhaps to your pager) that was brief, while the code could still send other errors to either the screen or logfile automatically.
The new $DEFIN
accomplishes a similar thing, but with input.
Currently, the special <> filehandle iterates through the command-line
files stored in @ARGV
. You can also use it to open STDIN
, but only
if @ARGV
is empty (it unshifts '-' onto @ARGV
). However, <> is
potentially more useful as a default input filehandle, much like a
simple "print" uses $_ for its default output:
@data = <>; # read from $DEFIN
The new $DEFIN
variable becomes what is read from when <> is used. It
should point to $STDIN
by default. This allows greater flexibility,
more consistency with operators like print, and doesn't require us to
reopen filehandles to get to different input sources:
# Figure out our input source based on what's open $DEFIN = $infile || $instream || $STDIN; @data = <>;
This allows valuable compartmentalization of code. The
simple <> construct is now a synonym for $DEFIN
. This
means that you can place something like the @data =
<>
line in a module, and allow the top-level code to change
the data source based on a simple assignment. You no
longer have to pass filehandles into functions or do
complex tie
manuevers to gain this ability.
Plus, with $DEFIN
, if you have a routine that must
read from $STDIN
, you can still do so easily by
explicitly saying:
$yes_or_no = <$STDIN>; # explicitly from $STDIN @data = <>; # read from $DEFIN
To retain the special behavior currently with <>, it is recommended that
Perl 6 require explicit use of $ARGV
:
while (<$ARGV>) { } # iterate through @ARGV
This has the benefit that it makes it more obvious that we're iterating
through @ARGV
. It also means that you can use <> to grab STDIN
while retaining the values in <@ARGV>, which is not possible currently.
The naming of the variables was chosen to make them
consistent with the Standard IO handles $STDIN
,
$STDOUT
, and $STDERR
.
Remove 1-arg select
. Make sure p52p6 translates select
to the new syntax.
Create $DEFOUT
variable and reset output functions to act on
$DEFOUT
instead of the default filehandle. $DEFOUT
should point to
the system's stdout by default, just like the default filehandle does
currently.
Add $DEFERR
variable and reset error functions (such as warn and die)
to print to $DEFERR
instead of STDERR
. $DEFERR
should point to
the system's stderr by default, just like STDERR.
Add $DEFIN
and change <> and other default input to work on
$DEFIN
. $DEFIN
should point to the system's stdin by default.
A basic Perl 5 implementation can be found as IO::Default at www.perl.com
The 1-arg select
syntax would have to be translated to something
like:
$DEFOUT = $FILE; # was select(FILE);
Since the only other thing that would really change would be the meaning of <> in loops, p52p6 would have to translate while(<>) to:
while (<$ARGV>) { }
However, when used outside of a loop, like so:
@file = <>;
No translation would have to take place since the usage would likely be
a shortcut to STDIN
(which would be retained since <> == $DEFIN
==
$STDIN
by default).
[1] This RFC does not take a firm stance on eliminating $|, $/, $\, and friends. However, this approach to handling default input and output does mean they are no longer needed. I do think they should go, but this RFC does not require it.
[2] It is possible in a way, but you can't print to STDERR explicitly, and you have to use a series of open calls. Better to just send it to the default error handle, whatever that may contain.
RFC 14: Modify open() to support FileObjects and Extensibility
RFC 30: STDIN, STDOUT, and STDERR should be renamed