Front page | perl.perl5.porters |
Postings from February 2016
signature subs and @_ semantics redux
Thread Previous
|
Thread Next
From:
Dave Mitchell
Date:
February 29, 2016 18:47
Subject:
signature subs and @_ semantics redux
Message ID:
20160229184742.GG29332@iabyn.com
On Thu, Dec 03, 2015 at 03:21:25PM +0000, Dave Mitchell wrote:
> What should the semantics of @_ be for a sub that has a signature?
Here's another stab at the semantics of @_ suppression and signatures.
I've divided it into two sections. First what I think think all the
issues and proposed solutions are (notionally without my personal
preferences/prejudices showing too much), and second, what I personally
think we should do.
Section I: the issues
=====================
A: what should @_ be/contain at runtime if suppressed? Note that this
issue can't be avoided, due to all the weird ways it can be accessed, such
as 'local *args = $::{_::};'. Options:
1 same as &f; i.e. remains that of the caller.
pros: it's the fastest option
cons: action at a distance; especially several nested @_-suppressed
subs, where the inner one will see the @_ of several
callers above.
2 On entry, each sub does the moral equivalent of
local *_ = $some_tied_array_that_croaks_on_access;
This special array might be either global, or unique to each sub
(like @_ is currently unique to each sub).
pros: at runtime, will quickly flush out unintended uses of @_.
cons: relatively slow to set up and take down;
has issues with '&foo' and '&goto foo' (see C below).
3 On entry, each sub does the moral equivalent of
local *_{ARRAY}'
which would initially leave the AV slot of PL_defgv as NULL.
Any usage of @_ within the sub would auto-vivify a new @_, which
would be discarded on sub exit. (Aristotle suggested this.)
pros: in terms of speed it's somewhere between A1 and A2;
it has an obvious behaviour with '&foo' and '&goto foo':
i.e. they're called with no args, or with whatever @_ has
been set to (also see C below).
cons: stray uses of @_ at runtime won't be detected (but see B below)
B: what should accesses of @_ in the sub do at compile time?
Aristotle suggested that in the presence of @_-suppression,
'use strict "vars"' should trap @_ usage; or to put it another way,
in that scope, @_ becomes a non-special-cased var for 'strict var'
purposes, and so needs to be referred to as @::_ or $::_[0] or 'our
@_' to avoid compile-time errors.
Although Aristotle didn't say it explicitly, I'm assuming that
this extends to things like 'shift' etc; so 'shift' within a sub is
assumed to be short for 'shift @_' and so gives a compile-time error
unless written explicitly as 'shift @::_'.
This of course doesn't cover all possible accesses of @_, but is
likely to pick up most of the common ones.
'&foo;' and 'goto &foo;', if regarded as being short for
'&foo(@_) and a hypothetical 'goto &foo(@_)', might also be
subject to this stricture: see (C) below.
pros: detects some common coding errors, especially where existing
code is being converted to signatures plus @_-supppressed.
cons: I dunno really - perhaps some people won't like writing
'@::_ = (1,2,3)'?
C: what should C<&foo;> and C<goto &foo> do under @_-suppression?
The two obvious sets of semantics are:
1 &foo is equivalent to &foo(@_), i.e. they use the current
value of @_, and whatever of A and B above are selected will
affect the compile-time and run-time behaviour of trying to use the
current value of @_.
2 Where @_ hasn't been modified, there is an implied assumption that
&foo and goto &foo reuse the args that were passed to the existing
sub; in that interpretation, with these:
sub bar($a,$b) { &foo; }
sub bar($a,$b) { goto &foo }
bar would be expected to pass ($a,$b) rather than () to foo.
The main con of (C2) is that it quickly becomes inconsistent:
sub bar($a,$b) { &foo; } # foo($a,$b)
sub bar($a,$b) { shift; &foo; } # foo($a,$b) or foo($b)?
sub bar($a,$b) { @=(1); &foo; } # foo($a,$b) or foo(1)?
The main pro of (C1) is that that is has a consistent behaviour;
it's main con is that people might think that (C2) is logical
behaviour. This might be less of an issue if the latter part of (B) is
implemented; that is, &foo; would be regarded as being equal to
'&foo(@_)'; and so dies under 'strict vars' unless explicitly written
as '&foo(@::_)' This would force coders to explicitly write what they
mean to happen.
'goto &foo;' is is more problematic as there's no way (as far as I'm
aware) to write 'goto &foo(@::_)'. So in this case people would
have to explicitly write
no strict 'vars'; goto &foo;
or
our @_; goto &foo;
3 Then there's the issue of whether the conceptual
local *_ = $some_tied_array_that_croaks_on_access;
or
local *_{ARRAY}
at the start of the sub should be unlocalised prior to doing goto
&foo.
D: should signatures and @_=suppression be orthogonal.
1 Various people have suggested that @_-suppression should be
orthogonal to signatures.
pros: the main advantage of this (if I'm summarising people
correctly) is that it allows, when converting existing code to
signatures, to say "this file has signatures but still has
@_", so that missed 'shift', '&foo' etc still work.
cons: extra complexity; (B) would already allow most such uses to be
detected at compile time
2 Assuming orthogonality, there's also the issue of whether
'use feature "signatures"' on its own should do @_-suppression;
i.e. the current default should change.
pros: when enabling signatures, people get the performance benefit
of @_-suppression by default;
cons: we're changing the existing behaviour of signatures (but
con con, they are experimental).
3 What should @_ suppression mean outside the scope of a sub?
Assuming it's done by a pragma, then what should this do?:
no snails; # or whatever the pragma is
sub f {
shift; # will do whatever we agree for A and B above
}
shift; # what happens here?
4 Assuming its orthogonal, what syntax should be used to enable
suppression?:
* a new pragma (to be be bikeshedded), e.g.:
no snails;
* a modification of the existing feature pragma; e.g.:
use feature 'signature'; # sigs and @_ suppressed
use feature 'arg_signature'; # sigs and @_
no feature 'arg_signature'; # no sigs; @_
no feature 'signature'; # no sigs; @_
NB: this wouldn't allow the 'no sigs, no @_ combo'
* a per-sub pragma, e.g.
sub foo :arg_array(1) {} # default - @_ is populated
sub foo :arg_array(0) {} # @_ isn't populated
sub foo($a,$b) :arg_array(0) {} # default: @_ isn't populated
sub foo($a,$b) :arg_array(1) {} # @_ is populated
* something in the signature syntax itself to indicate @_
should be provided for this sub.
This also wouldn't allow the 'no sigs, no @_ combo'
E: Under @_-suppression, would there be ways of accessing (parts of) the
sub's arg list?
1. We could not bother; if people really need it they can avoid
signatures.
2. It could be provided as a new field returned by caller()
(Note that caller already does something similar if called from the
package DB: it populates @DB::args with a (non-ref-counted) copy
of the caller's args, which is as buggy as a very buggy thing).
3. New ops could be provided which access arg elements or slices etc,
then XS modules could provide access.
F: For 5.24, should we warn in perlsub.pod and perldelta that @_ may
change?
Any changes aren't going to happen for 5.24, but if we reach rough
consensus here on what *will* change (especially if by default
signatured subs will no longer get @_), then should we update the docs
to warn people not to rely on @_ etc (or whatever it is we agree)?
Section II: What I propose.
==========================
My own personal preferences are:
A3 and B - i.e. roughly what Aristotle proposed. This doesn't have too bad
performance impact, and has consistent behaviour with just a couple of
simple rules to remember, which are:
In the scope of 'no snails' (or whatever),
1. Each sub has the logical equivalent of
local *_{ARRAY}'
at its start; so @_ starts off non-existent, and will be
autovivified if used, and freed at the end of the sub.
2. @_ is no longer special-cased as regards 'use strict "vars", and
so gives a compile-time error like any normal array unless
used as '@::_', $::_[0]', or 'our @_' etc.
Implicit uses of @_, such as in 'shift', '&foo' and 'goto &foo' are
treated as using @_ too, so they will need to be rewritten as
'shift @::_', '&foo(@::_)', 'our @_ = (,...); goto &foo' etc.
These would be coupled with C1, which then provides predicable behaviour
for things like '&foo;'.
For C3, the local '*_{ARRAY}' shouldn't be unlocalised when doing goto
&foo;
D: as for orthogonality, I still think its overkill, especially with
B providing compile-detection of most existing usages of @_ when converting
exiting files, but if people strongly want it, I won't stand in their way.
For D2, I am *strongly* of the opinion that 'use feature "signatures"'
by itself should do @_-suppression by default, without the need for a
further pragma.
I don't have a strong feeling for what the pragma/attribute or whatever
should look like.
If @_-suppression is in scope outside a sub, then I think if (B) is
selected, then 'use strict "vars"' should still apply. Other than that, the
pragma should have no effect.
E: ways of accessing caller args when @_ is suppressed.
I'm of the opinion that this is not needed; if people really want it,
via caller() or new ops or whatever, then I'd prefer this to be something
that could be done (preferably by someone else) at a later date; unless
people feel that the mechanism must be made available at the same time
that @_-suppression is added to core.
F: for 5.24, I think we should warn in perlsub and perldelta.
--
Dave's first rule of Opera:
If something needs saying, say it: don't warble it.
Thread Previous
|
Thread Next