[% setvar title Replace default filehandle/select with $DEFOUT, $DEFERR, $DEFIN %]

This file is part of the Perl 6 Archive

Note: these documents may be out of date. Do not use as reference!

To see what is currently happening visit http://www.perl6.org/

TITLE

Replace default filehandle/select with $DEFOUT, $DEFERR, $DEFIN

VERSION

  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

ABSTRACT

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.

NOTES ON FREEZE

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.

DESCRIPTION

$DEFOUT

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.

$DEFERR

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.

$DEFIN

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.

Naming

The naming of the variables was chosen to make them consistent with the Standard IO handles $STDIN, $STDOUT, and $STDERR.

IMPLEMENTATION

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

MIGRATION

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).

NOTES

[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.

REFERENCES

RFC 14: Modify open() to support FileObjects and Extensibility

RFC 30: STDIN, STDOUT, and STDERR should be renamed

www.mail-archive.com

www.mail-archive.com