The perils of & (and prototypes too!)
In a recent post, Chris K asks, why do I recommend using function()
rather than &function()
or &function
. I happened to see it right before heading to bed, but I wanted to respond, so who knows if this is a good example or not. Anyway here goes, look at this code (seen below if you have javascript), it prints 6 lines, do you know what they will be?
If you didn’t that’s ok, this is a bit of a toughie, and yes I threw in a gratuitous $"
for fun (which is the string used to join a quoted array). For those of you without a terminal handy, the output is:
hello world and good bye!
2
hello world and good bye!
hello world
hello world
good bye!
In my example mysub
enforces scalar context onto its argument. Now prototypes should not be used lightly, and I rarely recommend them, but when they are used, it is often for a good reason. In this case, the prototyped function is probably not what an author writing that function would have meant. (S)he probably meant, ‘this function only takes one argument’, and it kinda worked, but it didn’t.
Anyway, the first lesson is that calling a function with &
works, but it defeats prototypes. In the example the author was probably wrong to use prototypes, but in most cases, as I said, when a prototype is used, it is for a good reason, and defeating them should come with even MORE reason.
I have actually almost used that once, I wanted to make a clever wrapper of List::Util::reduce and to do so I would have had to defeated its prototype. The endeavor failed and so that code did not see the light of day, but it got close. Other than that case, I have never needed to defeat a prototype, and so I have never used &function()
in anger.
&function
is more clever and is used in some more common wrappings. It means call the function()
but with my current @_
. This isn’t as useful as its ‘twin’ goto &function
which mean, ‘stop this function and instead start doing function()
with my current @_ instead, and remove the current function call from the stack trace’. This is actually useful for function wrapping, say like in an AUTOLOAD
function. Again, though, you do this when you mean to, not all the time. I think I have perhaps used goto &function
a half-dozen times in anger.
The take-away message here is, in Perl 5 you should almost always be using function()
or function
(though the bareword is confusing in some cases, especially if you forget strict and warnings!). You should almost never need (and therefore not want to accidentally incur the potential side-effects) of using &
. They won’t happen often, but when you get into the habit of using &
and you do accidentally defeat a useful prototype, or unintentionally pass forward your @_
, it will make for a terrible debugging session.
Anyway, as always, happy Perling!
P.S. I am happy to see that people still find Learn to Spot a Good Tutorial useful, and it reminds me that it will soon need another version and year bump!
P.P.S, the site that hosts that article also contains a list of good tutorials, and some “not so good ones” too, so you can use/recommend them.
J
I did not know all of the outputs (haven’t been using prototypes so far). Great article!
There’s a good example of the difference between func() and &func in this regression I reported in the Perl 5.18.0 debugger: You could no longer see the value of @_ in the debugger, because this line: eval { &DB::save }; was replaced with this: eval { DB::save() }; https://rt.perl.org/Public/Bug/Display.html?id=118169