develooper Front page | perl.perl5.porters | Postings from May 2023

Re: Limitations of Attributes

Thread Previous | Thread Next
Dave Mitchell
May 5, 2023 10:59
Re: Limitations of Attributes
Message ID:
On Tue, May 02, 2023 at 02:33:04PM -0500, Ovid wrote:
> For example, attributes in signatures are a syntax error:
> sub foo ($bar :some_attr) { ... }

As a data point, this is a copy of my signature attributes proposal from
2019. It doesn't address stuff like being dynamic, but it contains a
useful description of how attributes are currently implemented.


=head1 Parameter Attributes

=head2 Synopsis:

    sub f ($x :foo, $y :bar(baz) bar2(baz2), ...) { ... }

    analogous to:

    my $x :foo;
    my $y :bar(baz) bar2(baz2);

We should support parameter attributes. I think this is a relatively
uncontroversial proposal.

What exactly should the semantics be? Lets first review the current syntax
as applied to 'my' declarations:

    my ($x, $y) :foo(foo_arg) :bar(bar_arg);

is roughly equivalent to

    use attributes ();
    my ($x,$y);
    attributes->import(, __PACKAGE__, \$x, "foo(foo_arg)", "bar(bar_arg)");
    attributes->import(, __PACKAGE__, \$y, "foo(foo_arg)", "bar(bar_arg)");

except that some attributes are built-in and are recognised and handled
directly by the lexer / parser, without ever getting

Note that says that attributes on variables are currently
experimental, although in practice we've supported things like
my $x : shared; for years.

As an aside, note that any argument to the attribute is scanned as a
single-quoted string - i.e. like q(...) - but is otherwise uninterpreted
by Perl itself.  Thus hypothetically a constraint expressed as an
attribute, e.g.

    sub foo ($x :where($x ne '(' ));
wouldn't get correctly parsed unless we handled it specially somehow,
which seems to be an argument for *not* using attributes for such things,
and instead use purpose-designed syntax, like, for example:

    sub foo ($x where $x ne '(', ...)

Second and subsequent attributes may be preceded by a colon, but don't
have to be: these are equivalent:

    my $x :foo :bar(1) :bar(2);
    my $x :foo  bar(1) :bar(2);

Thus for signatures, the obvious semantics would be that

    sub f ($a :foo, ...) {...}
is equivalent to

    sub f { my $a :foo; $a = $_[0]; .... }

The exact details of when attributes->import() is called is discussed in
the "Scope and Ordering" thread.

Once available, built-in attributes could in principle be used where Perl
6 uses traits, e.g.

    sub f($x is ro) { ... } # Perl 6
    sub f($x :ro)   { ... } # Perl 5 ???

See the "Aliasing and Read-only variables" thread for more detailed

Attributes can't be used on a placeholder parameter:

    ($x :foo) # ok
    ($  :foo) # error

Attributes can't be used with aliasing, except for slurpies (which alias
individual elements rather than the aggregate itself):

    (\$x :foo) # error
    (\@a :foo) # error
    (\%h :foo) # error
    (*$x :foo) # error
    (*@a :foo) # ok - like: my @a: foo;  \$a[0]  = ...;  \$a[1]  = ...
    (*%h :foo) # ok - like: my %h: foo;  \$h{..} = ...;  \$h{..} = ...

Note that in Perl 6 and some CPAN signature modules, the 'method' keyword
declares an implicit $self parameter, whose name can be overridden using
a postfix ':':

    method foo($x, $y)      { $self->{$x} = $y }  # implicit $self
    method foo($me: $x, $y) {   $me->{$x} = $y }  # explicit invocant

I have no plans to introduce such a 'method' keyword, but if we did,
we might need different syntax for the invocant, as the ':' would be
interpreted as the start of an attribute unless the toker was clever and
we are very careful that all signature syntax is capable of being


=head1 Scope and Ordering


=head2 Ordering of evaluation of terms

There is much external visibility, both from explicit execution of things
like default expressions and constraints, and implicitly from things like
FETCH(), overloaded stringify, and attribute handlers. The question is how
are these ordered, and what do we guarantee?

I propose that a few aspects of ordering are well defined; everything
else is left undefined, to allow us to change things in different releases
for the purposes of optimisation etc.

Within a single parameter element, we guarantee this order:

 1) attributes->import() is called as appropriate for any :attribute;
 2) the default expression (if present and needed) is run;
 3) the parameter variable is bound to its argument or default value;
 4) the constraint expression (if any) is run.

Between parameter elements, we guarantee that parameters are processed in
left-to-right order. 


Indomitable in retreat, invincible in advance, insufferable in victory
    -- Churchill on Montgomery

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About