TITLE

Short-circuiting built-in functions and user-defined subroutines

VERSION

  Maintainer: Garrett Goebel <garrett@scriptpro.com>
  Date: 6 Sep 2000
  Last Modified: 15 Sep 2000
  Mailing List: perl6-language@perl.org
  Number: 199
  Version: 4
  Status: Frozen

ABSTRACT

Allow built-in functions and user defined subroutines to catch exceptions and act upon them, particularly those emanating from control blocks. Put another way, allow blocks to simultaneously exit early with a value.

DESCRIPTION

OVERVIEW

Currently there is not a practical and flexible way to short-circuit built-in functions like grep and map which take blocks as a parameter.

A prime example that was raised is:

  my $found = grep { $_ == 1 } (1..1_000_000);

Under Perl 5, one would need to write something like:

  eval { grep { $_ ==1 and die "$_\n" } (1..1_000_000) }; 
  chomp(my $found = $@);

Under this current proposal this might be written as:

  my $found = grep { $_ == 1 and return $_ && last grep } (1..1_000_000);

  (or alternatively)

  my $found = grep { $_ == 1 and abort grep $_ } (1..1_000_000);

It is the opinion of the author that while the proposal saves little by way of typing, that it is minimally an improvement in clarity. Further this proposal introduces semantics which will make other tasks easier.

DEFINITION OF THE PROBLEM

  1. How do you simultaneously exit early and return a value?
      reject the current element and continue
      accept the current element and continue
      reject the current element and abort
      accept the current element and abort
      reject the current element and retry
      accept the current element and retry
  2. How do you do that *and* be consistent with existing semantics?

OPTIONS AND ALTERNATIVES

There has been much discussion and ideas exchanged on the best way to achieve this. Discussion ranging from specific solutions which would only apply to the grep and map built-ins, to more general ones such as unifying the exception syntax for loop, code, and bare blocks. There has also been a vocal contingent protesting that all of the suggestions attempt to shoehorn too much new functionality into existing semantics and that no one has yet presented any workable solutions.

The current ideas tend to converge around the following three options:

  1. Grant code blocks their names as labels, and add new keywords for code block control: continue/abort/retry:
      # short-circuit after first acceptance...
      $found = grep { ($_ == 1) and abort grep $_ } (1..1_000_000);
    
      # short-circuit after first rejection...
      $found = grep { ($_ == 1) and abort grep } (1..1_000_000);
  2. Grant code blocks their own names as labels, and the ability to catch next/last/redo exceptions which explicitly use those labels.
      # short-circuit after first acceptance...
      @smalls = grep { ($_ == 1) and return $_ && last grep } (1..1_000_000);
    
      # short-circuit after first rejection...
      @smalls = grep { ($_ == 1) and last grep } (1..1_000_000);
  3. The built-in function's operation should catch exceptions thrown from the block, where the exception thrown is true or false (alternative: 1 or false). Example:
      # short-circuit after first acceptance...
      $found = grep { ($_ == 1) and die 1 } (1..1_000_000);
    
      # short-circuit after first rejection...
      $found = grep { ($_ == 1)  or die 0 } (1..1_000_000);

INDECISIONS: BY THE PRICKINGS OF MY THUMB...

Detractors would argue:

  1. First Proposal: Add code labels and new keywords: continue/abort/retry
  2. Second Proposal: Add code labels, and let code blocks use next/last/redo syntax which explicitly use those labels.
  3. Third Proposal: Extend use of die

The author shares much of detractors opinions for the third proposal, and would prefer either the first or second to be implemented. The reason I prefer one of these two is that I believe they are more intuitive, flexible, and semantically rich. As such, the remainder of this RFC concern itself with issues pertaining to the first two proposals.

FINER DETAILS AND OTHER ISSUES

Issues common to the first and second proposals:

Issues specific to adding code block control statements

Issues specific to adding support for loop control syntax to code control

BACKWARD COMPATIBILITY ISSUES

If loop control syntax is grafted on for use with code blocks, then a Perl 5 -> 6 translator would need to explicitly label all loop blocks and loop control statements.

IMPLEMENTATION

I haven't a clue.

REFERENCES

Too many contributors to note them all down. The content of this RFC owes heavily to list discussions. The author has simply tried to pick and choose the best and most easily grasped ideas and analyses from the list.

RFC's that require this functionality

RFC 76: Builtin: reduce

Ideas that offer alternative routes to similar functionality:

RFC 22: Control flow: Builtin switch statement

RFC 31: Subroutines: Co-routines

RFC 123: Builtin: lazy

RFC 225: Data: Superpositions


the camel