6

The perl 5.18.2 documentation of splice has this example:

my(@a) = splice(@_,0,shift);
my(@b) = splice(@_,0,shift);

And I wonder: If @_ is evaluated before the shift there would be one item too much for the result to be correct. So conceptually the shift must be performed first. However in C they teach you that you should never rely on a specific order of evaluation of actual parameters (shift modifies @_).

So is that code only working because @_ is passed as a reference (to be able to change it), and the shift being evaluated before splice can access @_?

A Bit of History

I found the example goes some way back to 1996 (at least) and the book (page 219; a comment on page 535 suggests it may even come along from Perl 4):

WALL, Larry, Tom CHRISTIANSEN und Randal L. SCHWARTZ, 1996. Programming Perl. 2. Sebastopol, CA 95472, U.S.A.: O’Reilly & Associates, Inc. ISBN 1-56592-149-6

U. Windl
  • 3,480
  • 26
  • 54
  • I don't see that snippet in [splice](https://perldoc.perl.org/functions/splice). – choroba Apr 29 '22 at 09:15
  • @choroba It is in one of the earlier versions of that page: https://perldoc.perl.org/5.18.4/functions/splice and back. – TLP Apr 29 '22 at 09:45
  • Yes, and [here](https://github.com/Perl/perl5/issues/13321) the discussion that led to the change. – choroba Apr 29 '22 at 10:06
  • 1
    it's a weird way to write things, but you've correctly understood how this works – `splice(@arr, ...)` doesn't take the array by value but effectively by reference. Think of splice having a `(\@;$$@)` prototype. In the Perldoc, you can see this because splice is documented as taking an ARRAY (by-ref), not a LIST (by-value). – amon Apr 29 '22 at 10:46
  • @choroba From the link provided it seems that example code is a "Mr. Wall original" (and thus may not be changed) ;-) Well, it probably inspires thinking, but it's not the best example for programming style IMHO. – U. Windl Apr 29 '22 at 11:43
  • I'd say that the above comments settle it, specially when one reads through the p5p discussion linked by choroba (thanks, interesting :). Just to add a possibly useful tidbit: the prototype of `splice` (that amon's comment nicely uses) is found by `prototype "CORE::splice"` --> `+;$$@`. Here `+` means either `\@` or `\%` (array or hash name) – zdim Apr 29 '22 at 17:05

1 Answers1

3

Because @_ itself is being passed to splice (rather than being flattened or copied) because splice is expected to modify it.


With a normal subroutine, an array in the argument list would get flattened. But splice is an operator, so it's not limited to the behaviour of subroutines.

Perl provides the array to splice instead. And not a copy of it. The array itself is placed on the stack. This allows it to be modified, and it's more efficient as a bonus.

(In C terms, "the array" is a pointer. All Perl variables are pointers. The fact that only pointers are being exchanged under the hood is why Perl exhibits pass-by-reference semantics.)

Perl evaluates lists from left to right, so @_ is placed on the stack, then 0 is placed on the stack, then shift is evaluated modifying @_ and placing the removed value on the stack.

It doesn't matter that @_ is modified when it's found on the stack. As long as these things happen before the splice is performed — and they are — it works perfectly fine.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • I find these phrases rather confusing (after having changed a few others): "the array itself is placed on the stack" (when a reference is conceptually passed), "All Perl variables are pointers." (I'm afraid that would confuse most people thinking of `$a = $b`). Also the order of evaluation of the actual parameters is not relevant; what is important is the fact that parameters are evaluated *before* the function code is run (that is the array `@_` "had been shifted already"). (continues on next comment) – U. Windl Sep 08 '22 at 10:23
  • Also mentioning "the stack" and "found on the stack" probably confuses most people (even when it's the internal mechanism being used). -- Maybe you want to "touch up the answer a bit". – U. Windl Sep 08 '22 at 10:23
  • @U. Windl. Your edits were reverted since they were all incorrect: 1) Passing by reference is not the same thing as passing a reference. 2) "Sub" wasn't a reference to the keyword but short for subroutine. 3) There's no array reference. Splice does not receive an array reference. – ikegami Sep 08 '22 at 14:43
  • Re "*Also mentioning "the stack" and "found on the stack" probably confuses most*", The simple explanation is above the break. It's basically exactly the same, minus the parts that must necessarily mention the stack. – ikegami Sep 08 '22 at 17:21
  • My attempt to improve your answer may be wrong in your opinion, but in my opinion the answer is really hard to understand as you seem to refer to Perl implementation details (which seem to use phrases very uncommon to *users* of Perl). So IMHO either make the answer easier to understand by changing some phrases, or explain even more the fine differences of "references being passed" and "being passed by reference". Maybe I just lack basic understanding of `@_`, in which case you are free ad add another reference. Don't get me wrong: It's all to make *others* happy, too. – U. Windl Sep 09 '22 at 09:14
  • Re "*My attempt to improve your answer may be wrong in your opinion*", It wasn't a question of opinion. Your edits claimed things which were objectively wrong. – ikegami Sep 09 '22 at 13:44
  • Re "*you seem to refer to Perl implementation detail*", Well yeah. The question is asking about implementation details. So of course I explained implementation details. – ikegami Sep 09 '22 at 13:44
  • Re "*explain even more the fine differences of "references being passed" and "being passed by reference"*", The answer does not contain either of those sentences. Passing by reference is mentioned, but only in a footnote. Feel free to ignore the footnote. – ikegami Sep 09 '22 at 13:44
  • Re "*Maybe I just lack basic understanding of @_*", There's nothing special about `@_`. The answer is the same regardless of the array used. – ikegami Sep 09 '22 at 13:45
  • Re "*make the answer easier to understand*" What do you not understand? Maybe you need to write a your own version of `splice` and `shift` which take an array reference. You'll see `mysplice( \@a, 0, myshift( \@a ) )` works the same way as `splice( @a, 0, shift( @a ) )`. With `splice` and `shift`, the array itself is passed instead of an reference to it, but the reason they work the same is the same: the array isn't flattened or copied. – ikegami Sep 09 '22 at 13:47