Mailing List Archive

Tiny Signatures Extension Discussion
Back in 2019, Dave Mitchell shared the Jumbo Signatures extension discussion
<https://www.nntp.perl.org/group/perl.perl5.porters/2019/11/msg256677.html>.
That thread was amazing. Lots of great ideas.

*Lots* of great ideas means lots of work and makes it harder to get it
accepted. I want to start smaller. Stealing Borrowing some of his ideas,
but simplifying some of them (while allowing room for expansion), and
focusing on "high demand" features. Further, by starting smaller, we have
fewer bugs.

The features I want to focus on are:

1. Attributes for signatures variables
2. Named arguments
3. Aliasing

This is a small list, but focusing on those things that, er, I most want.
By having a smaller discussion, we have a better chance of moving forward,
and if we can get to an accepted PPC, we can give a strong signal to the
Perl community that we're moving forward.
<https://gist.github.com/#signature-attributes>Signature Attributes

This is on hold while Paul continues to work on feature-class
<https://www.nntp.perl.org/group/perl.perl5.porters/2023/08/msg266785.html>
<https://gist.github.com/#named-arguments>Named Arguments

For a first pass, named arguments and positional arguments would be
mutually exclusive. This is a limitation that can be lifted in future
versions, if needed. This has the advantage that (as far as I can tell),
it's forward-compatible with Dave's proposal.

sub orders(:$name, :$since //= undef, :@items) { ... }

The above would be sort of equivalent to:

sub orders {
my %arg_for = @_;
my $name = delete $arg_for{name} or die ...;
my $since = delete $arg_for{since};
my @items = exists $arg_for{items} && 'ARRAY' eq ref $arg_for{items}
? delete $arg_for{items}->@*
: die ...;
if ( keys %arg_for ) {
die ...; # unknown keys
}
...
}

Which would you rather write?

It would be called with a flat list:

my $orders = orders( name => $name, since => $date, items => \@items
);# ormy $orders = orders( name => $name, items => \@items );

If we have :$names, of course $arg_for{names} could be an array ref. :@names is
a useful convenience.
<https://gist.github.com/#aliasing>Aliasing

This would depend on attributes in signatures.

sub foo( $bar :alias ) { ... }

In the above, regardless of what $bar is, it's an alias to the argument
passed to foo, just as if we used $_[0] instead.

This one could *almost* be left off the list, since references are kinda
already aliases. However, when I'm walking and mutating complex data
structures, I don't want to handle the special case of non-reference versus
reference. I just want to know that I can mutate the calling value directly.

Generally speaking, this is a terrible idea. Practically speaking, it makes
some code much simpler and can be extremely performant.
<https://gist.github.com/#ppc>PPC?

Given that this is a much smaller set of suggestions, can we commit to
this? This isn't saying we do this now; it would be saying "we plan to do
this." Named arguments and aliases would go a long way towards killing off
@_ (not really removing it, but making it less much less likely to be
needed).

Not allowing positional and named arguments to be mixed might be
controversial, but I've found that having something like an "options" or "
extra" key works around that nicely.

This would be, like Corinna, an MMVP (a minimally minimal viable product),
with an eye towards later revisiting Dave's jumbo list. However, it would
send a strong signal to the Perl community that popular needs are being
addressed and Perl is maturing into a modern language.

Best,
Ovid

--
Curtis "Ovid" Poe
--
CTO, All Around the World
World-class software development and consulting
https://allaroundtheworld.fr/
Re: Tiny Signatures Extension Discussion [ In reply to ]
On Sat, 19 Aug 2023 16:56:05 +0200
Ovid <curtis.poe@gmail.com> wrote:

> The features I want to focus on are:
>
> 1. Attributes for signatures variables
> 2. Named arguments
> 3. Aliasing

Yup - all those three sound good.

Attribute syntax should fit just fine as currently a use of a colon is
an immediate syntax error; so adding

sub f($x :Wibble) { ... }
sub g($y :Blarg(stuff here)) { ... }

is no problem. Implementation-wise I don't know whether you'd just
invoke the current MODIFY_SCALAR_ATTRIBUTES behaviour that we all
know-and-hate.. that detail is to be determined. Alternatively it might
be a good place to continue the stuff I was looking at for the field
attributes.


Named arguments would fit nicely too - though if we're using a colon
there as well we just need to check there's no syntax ambiguity.
Probably it's fine because a comma has to sit between any two args, and
there's no point (?) putting an :Attribute on an unnamed $
placeholder...

sub h($a0, $a1 :Attr, :$a2, :$a2 :Attr) ...


Aliasing would be nice too. Behaviourally not hard to explain, my only
concern would be that whereas Dave's original suggestion involved using
a punctuation symbol to request it, I'd prefer it to be an attribute on
the parameter:

sub myopen($fh :alias, $mode, $path, @args) {
# can assign into $fh in here
}

(Such an attribute would, as a side-project, suggest the presence of a
:copy attribute that someone could put on a foreach loop to disable the
default-aliasing behaviour there;

foreach my $x :copy (@list) {
# mutations of $x do not appear in @list
}

But that's a thought for another day ;) )


> Given that this is a much smaller set of suggestions, can we commit to
> this? This isn't saying we do this now; it would be saying "we plan to
> do this."

On the overall basis above, I'd certainly be happy to take a look at a
PPC doc if one were written, and if we collectively manage to get it to
a position where we accept the idea, I'm sure I could find some time to
have a go at implementing it.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Tiny Signatures Extension Discussion [ In reply to ]
On Sat, Aug 19, 2023 at 5:09?PM Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> (Such an attribute would, as a side-project, suggest the presence of a
> :copy attribute that someone could put on a foreach loop to disable the
> default-aliasing behaviour there;
>
> foreach my $x :copy (@list) {
> # mutations of $x do not appear in @list
> }
>
> But that's a thought for another day ;) )


Maybe a thought for another day, but it reminds me of an issue that has bit
me a few times when I've been thinking about what we need for Perl 8.
Specifically, I would love to see:

1. A programmatically discoverable list of places where variables can be
declared *and* defined
2. A programmatic hook into those places

Off the top of my head, we have my, our, state, local, field, format, and
signatures. If we don't use strict, it's even more complicated.

(Note: I want format to die in a fire, but it's not a hill I'm willing to
die on)

I don't know exactly where I'm going with this. From P5P discussions, we
seem to want a definitive way of hooking into variables when they spring
into existence and we have many ways that can happen. Every time I come up
with a new idea, someone says "what about X?" and I realize I missed
something (as with your example). Another example:

open DONOTDOTHIS, '<', $filehandle;

This would lay the groundwork for more areas where we need to have "sticky"
behavior on variables ("types", though I hesitate to use the word) and
"infectious" behavior on variables (like tainting, but so far that's not
gotten anywhere). Combining those two features would give us a much larger
world of opportunities we can build on.

Best,
Ovid

--
Curtis "Ovid" Poe
--
CTO, All Around the World
World-class software development and consulting
https://allaroundtheworld.fr/
Re: Tiny Signatures Extension Discussion [ In reply to ]
>
>
> <https://gist.github.com/#named-arguments>Named Arguments
>
> For a first pass, named arguments and positional arguments would be
> mutually exclusive. This is a limitation that can be lifted in future
> versions, if needed. This has the advantage that (as far as I can tell),
> it's forward-compatible with Dave's proposal.
>
> sub orders(:$name, :$since //= undef, :@items) { ... }
>
> The above would be sort of equivalent to:
>
> sub orders {
> my %arg_for = @_;
> my $name = delete $arg_for{name} or die ...;
> my $since = delete $arg_for{since};
> my @items = exists $arg_for{items} && 'ARRAY' eq ref $arg_for{items}
> ? delete $arg_for{items}->@*
> : die ...;
> if ( keys %arg_for ) {
> die ...; # unknown keys
> }
> ...
> }
>
> What about not forcing anything, ie once function has signature, it can be
called with positional or named syntax ? (or even mixed).
See C# or Kotlin for examples?

Reposting short overview how other languages solve this problem, plus
syntax extension, which amongst others supports multiple
list parameters.

https://gist.github.com/happy-barney/15ff8755e7fd8ef94069ee4f667975df

Also, for named parameters we can use %_, eg:

# let assume named argument syntax extracts them into %_
sub orders {
my $name = delete $_{q ($name)} // shift // die ...;
my $since = delete $_{q ($since)} // shift // die ...;
my @items = exists $_{q (@items}) ? @{ delete $_{q (@items)} } : @_ ? @_
: die;
}

This would depend on attributes in signatures.
>
> sub foo( $bar :alias ) { ... }
>
> One small objection to name ... there is also demand for namespace
aliasing (and maybe more generic).
In this scope is using alias here good idea?

This is also only one side of problem, what about side of caller?

What about add capability to specify read-only / read-write attribute? They
both may act like suggested alias but also will signal
whether parameter is just parameter or additional return value.



>
> Generally speaking, this is a terrible idea. Practically speaking, it
> makes some code much simpler and can be extremely performant.
>
Terrible idea - agree.

Another case: what if function itself will not only treat parameter as
read-write but also as shared ?
Re: Tiny Signatures Extension Discussion [ In reply to ]
On Sat, Aug 19, 2023 at 04:56:05PM +0200, Ovid wrote:
> Back in 2019, Dave Mitchell shared the Jumbo Signatures extension discussion
> <https://www.nntp.perl.org/group/perl.perl5.porters/2019/11/msg256677.html>.
> That thread was amazing. Lots of great ideas.
>
> *Lots* of great ideas means lots of work and makes it harder to get it
> accepted. I want to start smaller. Stealing Borrowing some of his ideas,
> but simplifying some of them (while allowing room for expansion),

I'm starting to twitch slightly! Some of your proposals seem to be not
merely simplifications, but modifications, which could impede implementing
the "fuller" list later. But apart from that, thanks for kicking this.

All my comments below are intended to be constructive.

I'm not opposed in principle to modifications to my original proposals,
*but*, the original proposals were (over a long period of hard work)
carefully considered to work together in harmony, and be capable of
efficient implementation. I spent a lot of time considering many of the
edge cases that can arise when combinations of all these features are
added together.

I also have (or at least had) a good idea of how these could all be
implemented.

So I feel that any discussion of changes to the proposals need to be
considered carefully within the bigger picture, rather than a superficial
"ooh, that looks nice and straightforward".

> <https://gist.github.com/#signature-attributes>Signature Attributes

[. Just as a complete aside, call me old fashioned, but I can only discuss
things like this on a mailing list. The more people use fancy new tools
for collaboration, the less and less I interact. All the new PPCs have
almost completely passed me by because they're not being discussed on p5p.
]

Note that there can be two types of signature attributes: built-in and
user-defined. The former, (c.f. the built-in ':lvalue' for subs), are
spotted at compile-time by the parser and handled specially as
appropriate. User-defined attributes involve the parser planting run-time
code to call out to a user-defined method. The latter opens up a big can
of worms - e.g things like the ordering of the calling of user-defined
code across different parameters and named parameters and default values.
These issues are solvable, but they need to be agreed. For example what
guarantees (if any) do we give about the order in which named parameter
attributes are called. If we define an order, will that restrict what we
can do in the way optimisation? And so on.

There's no reason why we can't just allow built-in attributes for now
(:aliases, :ro for example) and leave the more complex user-defined
attributes for another day.

> For a first pass, named arguments and positional arguments would be
> mutually exclusive. This is a limitation that can be lifted in future
> versions, if needed. This has the advantage that (as far as I can tell),
> it's forward-compatible with Dave's proposal.

Yes, its forward compatible, although you also need to consider whether
to allow a final slurpy parameter too, and if so how it is handled.

But my gut feeling is that allowing both positional and named parameters
wouldn't be hard, and wouldn't make implementing named parameters any more
tricky. And allowing positional parameters would allow methods to use
named syntax while having a positional $self parameter.

> It would be called with a flat list:
>
> my $orders = orders( name => $name, since => $date, items => \@items

I think having named array / hash parameters needs careful consideration.
This wasn't in my original proposal. I suggested instead that passed
aggregate references could be derefed and aliased using an appropriate
aliasing syntax (see further below for more on aliasing).

So my proposal aliases the array:

sub f {\:@a) {...} f(a => \@array)

which is equivalent to

use feature qw(refaliasing declared_refs);
my \@a = \@array;

while your proposal (IIUC) makes a copy of every element of the array,
which could be costly.

sub f {:@a) {...} f(a => \@array)

which is equivalent to

my @a = @array;

It also seems to be incompatible with positional parameter syntax: i.e.
a named array parameter does a dereference and copy, while a positional
array parameter is a slurpy and has completely different behaviour.

My original proposal discussed a number of issues with named parameters,
which any new proposal needs to address. Off the top of my head:

* how is a final slurpy parameter handled, both syntactically and
semantically? In the absence of such, is an unrecognised named arg a
run-time error?
* how are duplicate name args handled?
* croak if not an even number of args?
* can some names be declared mandatory (even if not in the initial
implementation)?
* the order in which named default expressions are called, and
whether rightward ones can refer to parameter lexicals declared
leftwards, even if the actual args are supplied in a different order.

> <https://gist.github.com/#aliasing>Aliasing

> This would depend on attributes in signatures.

Although as mentioned above, we only need to implement built-in attributes
for now, which is a simpler challenge.

> sub foo( $bar :alias ) { ... }
>
> In the above, regardless of what $bar is, it's an alias to the argument
> passed to foo, just as if we used $_[0] instead.
>
> This one could *almost* be left off the list, since references are kinda
> already aliases. However, when I'm walking and mutating complex data
> structures, I don't want to handle the special case of non-reference versus
> reference. I just want to know that I can mutate the calling value directly.

This seems to be a glossing over a lot of details. In my proposal, I
identified potentially two different types of aliasing: one more useful
for scalars, the other more useful for aggregates, but in principle both
types could be supported for all.

* Direct aliasing

For scalars: the equivalent of

use feature qw(refaliasing declared_refs);
my \$x = \$_[0]; # $x is a rw alias of the first arg

For arrays / hashes:

The individual elements of the array / hash would be aliased to
the passed args - i.e. a like slurpy, but not copying. This would
provide a way of efficiently providing the equivalent of @_ to those
who need it.

* Reference aliasing

Arguments which are references to arrays or hashes (or for
completeness, to scalars) would be auto-dereferenced and aliased (not
copied) to a aggregate parameter. So

sub f {\%h) {...} f(\%hash}

is equivalent to

use feature qw(refaliasing declared_refs);
my \%h = \%hash;

So we need to be clear whether we're happy supporting only one type of
aliasing behaviour, and if so, which type we prefer. Or some sort of
mixture?

Also we need to be clear whether we want the aliasing to be for:

1) Convenience: e.g. so we can access a passed array ref as '$a[...]'
within the body of a sub, rather than having to do '$a->[...]'
everywhere.

2) Performance: not having to make a copy each arg, even if it's only used
once. Hence why people write code like sub foo { $_[0]->{foo} = $_[1] }
It would be nice to write code which performs as well but is readable.

3) Mutability - sometimes you actually want to modify $_[0], although I
suspect this is not very often in practice. But it at least needs to be
allowed for.

Note that I originally also proposed a :ro attribute, which could be
applied to *any* lexical variable declaration (not just signature
attributes), which would make it compile-time error to use that variable
in potential lvalue context (such as $x = 1; foo($x); for ($x) { ...} ).
This could be implemented fairly easily, and would make it less
objectionable to use aliases purely for performance reasons.

I'm not too hung up on the exact syntax for aliasing, although we should
bear in mind Larry's Huffman Coding principle: the common stuff should
be the short stuff.

For example, the default in Raku (IIRC) is that parameters are read-only
aliases. This is a sensible default, as it's both efficient and useful.
We can't do that in perl5, because read-write copies has been the default
for too long. But we aught to make declaring something similar in perl5
be as painless as possible. So (hypothetically)

sub foo($self :alias :ro, $x :alias :ro, @y :alias :ro) { ... }

is the sort of boiler-plate mess that we expect from the Java's of this
world, and is seems unperlish.

I toyed with the idea of making :ro the default when declaring an alias,
and so you would have to explicitly declare :rw instead.

I quite liked using a single punctuation symbol for indicating aliasing,
i.e. *$x is a direct alias, and \@y is a reference alias. Perhaps a bit
line-noisy, but it would mean the 'foo' sub above would be declared as:

sub foo(*$self, *$x, \@y) { ... }

which to me seems a lot easier on the eye. By using punctuation rather
than attributes, it makes the names of the variables stand out.

Note I'm not saying at that all of this aliasing syntax needs to be
implemented at once; merely that we need to agree what the long-term goal
is, so that implementing a subset of the aliasing proposal initially
doesn't rule out later additions.

> Given that this is a much smaller set of suggestions, can we commit to
> this? This isn't saying we do this now; it would be saying "we plan to do
> this." Named arguments and aliases would go a long way towards killing off
> @_ (not really removing it, but making it less much less likely to be
> needed).

I'm happy with 'important' extra features being added soon piecemeal,
as long as as we never lose sight of the big picture in the quest for
simplicity and speed of implementing. And as long as the internal
implementation takes account of how it will be later expanded.

My work plan has (for a while) been to finish or the refcounted-stack
work, then start on signatures. The RC work has (as is always the case)
taken a lot more time than I expected.

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