[% setvar title Function-call named parameters (with compiler optimizations) %]

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

Function-call named parameters (with compiler optimizations)

VERSION

  Maintainer: Michael Maraist <maraist@udel.edu>
  Date: 25 Aug 2000
  Last Modified: 30 Sep 2000
  Mailing List: perl6-language-subs@perl.org
  Number: 160
  Version: 3
  Status: Frozen

CHANGES

Finialized various features by removing many of the options( grealy simplified the RFC). Unified the goals with that of RFC 176 and RFC 273.

ABSTRACT

Function parameters and their positions can be ambiguous in function-oriented programming. Hashes offer tremendous help in this realm, except that error checking can be very tedious. Also, hashes, in general, take a performance hit.

The goal is to enhance functionality / convinience / performance where possible in regards to named-parameters, with a minimal of changes. And, at the same time, allow this to be a completely optional and virtually transparent process. The following is an in-depth analysis of various ways of accomplishing these goals.

DESCRIPTION

The current method of parameter proto-types only fulfills a tiny niche, which is mainly to offer compile-type checking and to disambiguate context ( as in sub foo($) { }, or sub foo(&$) { } ). No support, however, is given to hashes, even though they are one of perl's greatest strengths. We see them pop up in parameterized function calls all over the place (CGI, tk, SQL wrapper functions, etc). As above, however, it is left to the coder to check the existance of required parameters, since in this realm, the current proto-types are of no help. It should not be much additional work to provide an extension to prototypes that allow the definition of hashes.

The following is a complex example of robust code:

 #/usr/bin/perl -w
 use strict

 # IN: hash:
 #         a => '...' # req
 #         b => '...' # req, defined
 #         c => '...' # req, 0 <= c <= MAX_C
 #         d => '..'  # opt
 #         e => '..'  # opt
 #         f => '..'   # opt
 # OUT: xxx
 sub foo {
  my $self = shift;
  my %args = @_;

  # Requires $a
  my $a;
  die "No a provided"
     unless exists $args{a};
  $a = $args{a};

  # Requires non-null $b
  my $b;
  die "invalid b"
     unless exists $args{b} && defined ($b = $args{b});

  #  Requires non-null and bounded $c
  my $c;
  die "Invalid c"
     unless exists $args{c} && defined ($b = $args{b}) && ($c >= 0 && $c <
$MAX_C);

  my ( $d, $e, $f ) = @args{ qw( d e f ) };
  ...
 } # end foo

Becomes:

 sub foo($%) : method required_fields(a b c) fields(d e f) doc(<<EOS) {
 # IN: hash:
 #         a => '...' # req;  Do some A
 #         b => '...' # req, defined; Do some B
 #         c => '...' # req, 0 <= c <= MAX_C; Do some C
 #         d => '..'  # opt; Do some D
 #         e => '..'  # opt; Do some E
 #         f => '..'   # opt; Do some F
 # OUT: xxx
 EOS
   my $self = shift;
   my %args : fields(a b c d e f) = @_; # produce optimized hash
    that is already pre-allocated at compile-time.

   # Requires non-null $args{b}
   die "invalid b"
      unless defined $args{b};

   # Requires non-null and bounded $args{c}
   die "invalid c"
      unless defined $args{c} && ($args{c} >= 0 && $args{c} < $MAX_C);

   ...
 } # end foo

 $obj->foo( c => 3, b => 2, f=> 8, a => 1 );
 # Note the out-of order, and the mixture of optional fields

 foo( $obj, a => 1, b => 2, c => 3 ); # still totally legal
 foo( a => 1, b => 2 ); # compiler-error (invalid num-args)
 foo( 1,2,3,4,5,6,7); # compiler-error, missing args a, b and c
 foo(a,1,b,2,c,3,$obj); # compiler-error, missing args a, b and c
 # (since they're offset by one)
 my @args = ( a => 1, b => 2, c => 3);
 $obj->foo( @args ); # checking-deffered to run-time.  Will be ok.
 my @bad_args = ( b => 8, e => 4 );
 $obj->foo( @bad_args ); # checking-deffered to run-time.  Will fail.

Essentially, perl's compiler can be put to use for hashed-function calls in much the same way as pseudo hashes work for structs/objects. Making this a compile-time check would drastically reduce run-time errors in code (that used hash-based parameters). It would also make the code both more readible AND more efficient.

For readibility, perl can be quiried for the list of allowable options as well as general documentation. In the above, the listing of Input options would have been redundant, for both the code-reader, and the run-time query, but was provided for completeness.

Note also that the above is compatible with the existing structure. In fact, foo required the old-style prototype to distinguish the "self" variable from the general-hash arguments. The use of the attribute "method" was optional, and could be used in the auto-generation of a $SELF variable. At the very least, it allows a run-time description of what the first argument really-is.

An important thing to note is that we're not changing the functionality of execution. Perl sub's still look and feel like old-style subs to the user. They simply act as if additional run-time checking has occured. The only physical difference is that, where possible, you will find compile-time errors.

In the case that a static parameter list is provided (no dynamically expanded array's / hashes), and the strict-hash method is used (see RFC 273), then a compile-time reordering can occur, which has the effect of an array copy of values instead of the generation of a hash. So long as the subroutine never makes use of dynamic field lookup, a hash is never used (except for a behind-the-schenes mapping of dynamic parameters).

This helps large-scale functions, such as TK / CGI / etc, where there are dozens of optional parameters that should all be called explicitly in the code.

Proposed attributes for subroutines are as follows:

As above, The allowable fields is the union of fields(..) and required_fields(...). The text representing "..." is treated just like qw(...) as would be use vars qw(...). This method allows extensions that could more finely define data-types. For example, it is possible (though not currently proposed) that these attribute lists could do the following:

 sub foo : required_fields( $cnt @list %data ) {
   my %args: fields(cnt list data) = @_;
   print "Cnt = $args{cnt}\n";   # cnt garunteed to be a scalar
   for my $item ( @{$args{list}} ) { ... } # list garunteed to be an array ref
   while( my ( $key, $val ) = %{$args{data}} ) { ... }
     # data garunteed to be a hash-ref
 }

 my %data = ( a => 1, b => 2 );
 foo( cnt => 5, list => [ 1, 2, 3 ], data => \%data );

The lack of any prefix would mean that any data-type would be allowable.

IMPLEMENTATION

The compilation and run-time code would have to be augmented to handle the restrictive field-attributes.

SUMMARY

In summary, in keeping with perl's spirit, we should definately not enforce a new function / method invocation process; not even for hash-based / named parameters. Also, it makes little sence to produce an entirely new syntax for one or two special cases, which only obtain performance benifits under certain conditions. This would needlessly produce legacy code which would be difficult to maintain in the future.

This RFC suggests a compatible method of named-parameters through the use of an optional compiler-level hash. Initial implementations could all be applied as a form of pre-processor. Subsequent versions could internally optimize various special cases. The use of attributes is a logical extension, since we first define the core of an entity, then extend the description for the purposes of optimization and narrowing of correctness of code.

This RFC made heavy reference to the proposed built-in-pseudo-hash or strict-hash described in RFC 273, but the beauty is that that just about everything is independant and thus optional. The following is the minimum defined by this RFC:

 sub foo : fields( a b c) {
  # uses @_ just as before
 }

 foo( @args ); # so long as args defines the hash ( a=> 1, b => 2, c => 3 )

The following is the ideal case (allows all possible optimizations:

 sub foo($%) : fields( $a %b @c ) required_fields( $a ) doc("useful stuff")
{
   # uses @_ just as before
   my $scalar = shift;
   my %args : fields ( a b c ) = @_;
 }

 foo( $scalar, a => 5, b => { .. }, c => [.. ] );

REFERENCES

Thread

man perlref: pseudo-hashes

RFC 273: Internal representation of Pseudo-hashes using attributes.

RFC 176: subroutine / generic entity documentation

RFC 57: Subroutine prototypes and parameters

RFC 75: structures and interface definitions

RFC 152: Replace invocant in @_ with self() builtin