| perl6 Short-circuiting built-in functions and user-defined subroutines |
Short-circuiting built-in functions and user-defined subroutines
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
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.
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.
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
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:
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);
# 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);
# 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);
Detractors would argue:
continue/abort/retry
next/last/redo syntax which explicitly use those labels.
die
die into something that can be automatically caught by built-ins? I.e., no good support for multi-level exceptions.next/last/redo control as is available with loop blocks.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.
It might therefore follow that it would be good to define goto foo to have the same semantics as goto &foo when the label referred to is a function. Namely to pass @_. It would then be possible if desired to deprecate goto &foo.
caller. 0 refers to the current code block, 1 the caller's, and so forth. Otherwise undef may also be used to refer to the current code block.
Regardless of whether or not this proposal is implemented, it might be nice to add the same numeric label semantics when labels are used with loop control statements.
Examples:
sub func1 { @_ ? &func2 : abort undef, 0 }
sub func2 { $_[0] eq undef ? abort func1, 0 : &func3 }
sub func3 { $_[0] eq 'foo' ? abort 2, 0 : 1 }
sub foo { # Code Block
eval { # Bare Block
for (...) { # Loop Block
last && return 1; # Note: all
die && return 1; # of these go
abort 1, 1 # to different
return 1; # places
}
};
}
This example doesn't confuse the author. But that isn't to say that others won't find it confusing.
return $value && next LABEL be used to support both short-circuiting and returning a value
There was some consensus that return is a good fit for returning a value from a control block. But it doesn't support multi-level escaping. Therefore the use of loop control syntax is tacked on. The question becomes if both return and last are both exceptions which exit the block early, how do you catch them both and handle them appropriately? Is && sufficient to the cause? From the author's perspective it looks fine and dandy. But I have little or no idea how this would work in regard to Perl internals.
die && return $valueIf 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.
I haven't a clue.
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 76: Builtin: reduce
RFC 22: Control flow: Builtin switch statement
RFC 31: Subroutines: Co-routines
RFC 123: Builtin: lazy
RFC 225: Data: Superpositions
|
Perl.org sites
: bugs
| dev
| history
| jobs
| learn
| lists
| use
Site Information and Contacts |
|