Wednesday, November 16, 2005

Perl subroutine parameter passing - again


One of the reasons Damian Conway does not like prototypes is that (his words) they do not give the results expected. Well, what results do you expect when you don't use them?

It is sometimes said that parameters are passed into Perl subroutines by copy, and some say they are passed by reference. In fact they are passed by magic, and as we all know, any technology sufficiently advanced is indistinguishable from Perl.

What do you expect to be printed?

sub mysub
{
@_ = qw(one two three)
}

@array = qw(The quick brown fox);
mysub (@array);
print "@array\n";

We get 'The quick brown fox '. The array is unchanged, so you might think the array is passed by value. The call:

mysub (qw(The quick brown fox));

also works fine, so no problem. Let's change the subroutine to use a slice instead:

sub mysub
{
@_[0,1,2] = qw(one two three)
}

Now we get 'one two three fox', the array is changed! Changing the elements changes the caller's array, changing the whole array does nothing! Now the line:

mysub (qw(The quick brown fox));

fails with "Modification of a read-only value attempted…"

This is a run-time error, whereas if you use a prototype:

sub mysub (\@)

we get a compile-time error:

"… must be an array (not list)…"

If I preferred run-time over compile-time errors I would be using the Korn shell.

Once the prototype has been specified it does not matter if we alter the caller's array in total or use a slice – the caller's code is always the same.

OK, I admit that altering @_ direct is damn awful code, and it breaks another of Damian's rules. He's not all bad ;-)

I don't believe that an inconsistency in one part of Perl (prototypes) is a justification to switch to an inconsistency in another (pass by magic). Just admit the inconsistencies and celebrate them – or move to Perl 6.