RFC 56 (v3) Optional 2nd argument to C<pop()> and C<shift()>

[email protected] (Perl6 RFC Librarian)
Newsgroups perl.perl6.language,perl.perl6.announce
Message-ID <[email protected]>
This and other RFCs are available on the web at
  http://dev.perl.org/rfc/

=head1 TITLE

Optional 2nd argument to C<pop()> and C<shift()>

=head1 VERSION

  Maintainer: Jonathan Scott Duff <[email protected]>
  Date: 7 Aug 2000
  Last-Modified: 1 Sep 2000
  Version: 3
  Mailing List: [email protected]
  Number: 56
  Status: Developing

=head1 ABSTRACT

The inverse operations to C<pop()> and C<shift()> both accept a LIST to
"add" to an array, yet C<pop()> and C<shift()> only remove B<one> element
from an array.  In the interest of symmetry and TMTOWTDI, C<pop()> and
C<shift> should allow the programmer to remove multiple items from an
array.

=head1 DESCRIPTION

The intent should be obvious, but I'll point it out anyway.

=head2 pop

The documentation for Perl 5.6.0 states that pop has one of two forms:
C<pop ARRAY>, or just C<pop>.  This RFC proposes that a third form be
added to C<pop()>

=over 4

=item pop ARRAY, EXPR

EXPR would be evaluated to determine the number of elements to remove
from the end of ARRAY and that number would be removed and returned.
Thus C<pop()> would be a more natural inverse to C<push()> (If you can
add multiple elements to an array with C<push()>, why can't you remove
multiple elements from an array with C<pop>?)

=back

This functionality can currently be accomplished with C<splice()>, but
it is non-obvious that C<splice()> should be the routine to call and
the method isn't at all intuitive.  To "pop" the last $N items off of
the end of an array using C<splice()>, the call looks like this:

	splice @array, -$N;		# remove the last $N items

contrast to the more natural looking

	pop @array, $N;			# remove the last $N items

Aaron J Mackey <[email protected]> asked whether or not this should
really be equivalent to:

	reverse splice @b, -$N; 	# As if we'd popped each individually

I think that the following should hold:

	@a = pop @b, $n;
	push @b, @a;			# @b now in its original state.
	
Thus, the 2 argument version of C<pop()> should treat the C<$N>
elements as a group.

=head2 shift

The semantics for C<shift()> are similar to C<pop()> except that it
operates on the other end of the array.  C<shift()> also suffers from
the inability to shift more than one element from the array.  Just
like C<pop()>, the two forms of C<shift()> are a C<shift ARRAY>, and
just plain C<shift>.  This RFC proposes that a third form be added:

=over 4

=item shift ARRAY, EXPR

EXPR would be evaluated to determine the number of elements to remove
from the beginning of ARRAY and that number would be removed and returned.
Thus, C<shift()> would be a more natural inverse to C<unshift>.  (If
you can add multiple elements to an array with C<unshift()>, why can't
you remove multiple elements with C<shift()>?)

=back

As with C<pop()> the proposed semantics can be accomplished with
C<splice()> and are just as un-intuitive:

	splice @array, 0, $N;		# remove the first $N elements

contrast to

	shift @array, $N;		# remove the first $N elements

Again, as with C<pop()>, C<shift()> should treat the first $N elements
of the C<@array> as a group so that the following holds:

	@a = shift @b, $N;		# remove the first $N elements
	unshift @b, @a;			# put them back
					# @b is unchanged

=head2 Random examples

	@numbers = 1..10;
	$ten = pop @numbers;		# still works
	@popped = pop @numbers, 3;	# Take away 7, 8, 9
	push @numbers, @popped;		# Put 'em back
	@popped = pop @numbers, 0;	# Nothing happens
	@popped = pop @numbers, 9;	# And then there were none.

	@numbers = 1..10;
	@popped = pop @numbers, 100;	# And then there were none but
					# @popped only has 10 things

	@numbers = 1..10;
	$one = shift @numbers;		# still works
	@shifted = shift @numbers, 3;	# Take away 2, 3, and 4
	unshift @numbers, @shifted;	# Put 'em back
	@shifted = shift @numbers, 0;	# Nothing happens
	@shifted = shift @numbers, 9;	# And then there were none.

	@numbers = 1..10;
	@shifted = shift @numbers, 100;	# And then there were none but
					# @shifted only has 10 things

=head1 IMPLEMENTATION

I don't know the gory details other than it should be possible.
However, there is one implementation detail that occurs to me:
What should happen when the expression given to C<pop()>, or
C<shift()> evaluates to a negative number?  I see three options:

	1) Nothing.  We can only pop/shift positive amounts
	2) Act as if the number were positive  (i.e. pop @array, abs(EXPR))
	3) C<pop()> would then act as C<shift()> and C<shift()> would
	   act as C<pop()>

I propose that option #3 be adopted since it seems the most Perlian
and so far no one has disagreed.  :-)

=head1 MIGRATION

Gisle Aas <[email protected]> mentioned that the behavior of code
such as this:

	foo(pop @a, "bar");

would be changed by this proposal and that the perl5 to perl6
converter would need to handle this case.  Similar provisions would
need to be made for the proposed 2-argument C<shift()>

=head1 ISSUES

Several people have commented on the proposed return values for the
new forms of C<shift> and C<pop>.  The author of this RFC continues to
maintain that the following identities should hold:

	push(@array, pop(@array, $N));		# @array is unchanged
	unshift(@array, shift(@array, $N));	# @array is unchanged

=head1 REFERENCES

The Perl 5.6.0 documentation
L<perlfunc/pop>
L<perlfunc/shift>
L<perlfunc/splice>

Comments on return values
http://www.mail-archive.com/[email protected]/msg01306.html
http://www.mail-archive.com/[email protected]/msg02465.html

Perl5 -> Perl6 translation issues
http://www.mail-archive.com/[email protected]/msg01328.html
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.