Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Jumbo Signatures extensions discussion

by Corion (Pope)
on Nov 29, 2019 at 07:14 UTC ( #11109414=perlmeditation: print w/replies, xml ) Need Help??

dave_the_m has posted a long and very comprehensive thread to perl5-porters in which he outlines his plans for subroutine signatures. The plan is to get a consensus on the syntax of the various features and then to implement them over the next year. The goal is to have subroutine signatures out of experimental status by Perl 5.36. Whatever breaking of backwards compatibility with the existing (experimental) subroutines is necessary should go into 5.32.

I have mostly copied the Synopsis section of each mail here, and I really encourage you to look at the mails themselves which contain far more discussion of the proposed behaviours.

The separate threads are:

Jumbo Signatures extensions discussion - the top level thread

Here is a contrived example of a signature which demonstrates many of the proposals. Some of it will make more sense after each proposal has been read.

sub foo ( $self, # parameter declarations starting with '?' examine, # but don't consume any further arguments: ?*@args, # @args is set to the equivalent of @_ with one # arg shifted; i.e. like a slurpy but peeking ahead # and not actually consuming any args; the '*' means # that each @args element is aliased to a passed arg ?$peek_a, # $peek_a is bound to next arg but without consuming + it ??$has_a, # $has_a is set to true if there's an argument for $ +a $a, # normal scalar parameter ?{ print $a },# embedded code block - runs code; doesn't shift arg +s Dog $spot, # works the same as 'my Dog $spot' $b :shared, # you can use normal variable attributes $c :ro, # at compile time, $c in lvalue context croaks \@ary, # aliases @ary to a passed array reference \%hash, # aliases %hash to a passed hash reference *$scalar, # aliases $scalar to the next argument # Constraints and coercions: $d!, # croak if $d not defined $e isa Foo::Bar, # croak if $e isn't of that class $f is Int where $_ > 0, # croak if $f not a positive integer $x = 0, # a default value $y?, # short for $y = undef \@array?, # short for \@array = [] :$name1 = 0, :$name2 = 0, # consume name-value pairs (name1 => ..., name2 => . +...) @rest # slurp up any remaining arguments ) { .... }

Parameter Attributes

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

analogous to:

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

Perl should support parameter attributes.

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_ar +g)"); attributes->import(, __PACKAGE__, \$y, "foo(foo_arg)", "bar(bar_ar +g)");

Named Parameters

sub foo ( $pos1, # positional parameter; consumes 1 arg $pos2, # positional parameter; consumes 1 arg :$name1, # named parameter, consumes ("name1", value) arg +pair :$name2, # named parameter, consumes ("name2", value) arg +pair @rest, # consumes all unrecognised name/value pairs ) { ... }

This seems a popular feature request: give Perl 5 something similar to Perl 6's named parameters.

Querying Parameters

?$x peek ahead to the next arg ??$x peek ahead and see if there is a next arg ?@a peek ahead and copy all remaining args ?%h peek ahead and copy all remaining key/val arg pairs ?{ code } execute some code without consuming any args

Sometimes you want to find out some extra information about the arguments and the state of argument processing. With @_ available, this can be done, if messily; if @_ isn't populated (see the "@_ Suppression" thread), then it becomes harder/impossible, unless some other mechanisms are added.

@_ suppression

@_ will not be set, unset or localised on entry to or exit from a signatured sub; it will still be the @_ of the caller. But any use of @_ within the lexical scope of sub a sub will warn.

Aliasing and Read-only variables

sub foo { # the lexical vars $a, @b and %c are aliased to the things poi +nted # to by the reference values passed as the first three argumen +ts: \$a \@b, \%c, # $d is aliased to the fourth arg: *$d, # $e is aliased to the fifth arg, but at compile time, any us +e # of $e in lvalue context within this lexical scope is a compi +le # time error: *$e :ro, # the elements of @f are aliased to any remaining args, # i.e. a slurpy with aliasing ...: *@f # .. or a slurpy hash; every second remaining arg is aliased t +o # the hash's values: *%g ) { ... }

Type and Value Constraints and Coercions

sub f( $self isa Foo::Bar, # croak unless $self->isa('Foo +::Bar'); $foo isa Foo::Bar?, # croak unless undef or of tha +t class $a!, # croak unless $a is defined $b is Int, # croak if $b not int-like $c is Int?, # croak unless undefined or in +t-like $d is PositiveInt, # user-defined type $e is Int where $_ >= 1, # multiple constraints $f is \@, # croak unless array ref $aref as ref ? $_ : [ $_ ] # coercions: maybe modify the +param ) { ...};

It seems that people express a desire to be able to write something like:

sub f (Int $x) { ... }

as a shorthand for for something like:

sub f ($x) { croak unless defined $x && !ref($x) && $x =~ /\A-?[0-9]+\Z/; ....; }

Similarly people want

sub f (Some::Arbitrary::Class $x) { ... }

as a shorthand for

sub f ($x) { croak unless defined $x && ref($x) && $x->isa(Some::Arbitrary::Class); ...; }

Furthermore, there are also Perl 6 generic constraints:

sub f ($x where * < 10*$y) { ... }

Scope and Ordering

We need to determine and document how the various parts of a signature behave as regards to lexical scope, visibility, tainting and ordering of things like default expressions and constraints.

dave_the_m proposes for lexical scoping that:

sub f($a, $b, $c, ... ) { BODY; }

Is logically equivalent to:

sub f { my $a = ....; my $b = ....; my $c = ....; ....; BODY; }

In particular, each parameter element is independent taint-wise, and each parameter variable has been fully introduced and is visible to default expressions and the like in further parameters to the right of it.

Miscellaneous suggestions

This last post contains are a few random suggestions that people have made at various times.

Replies are listed 'Best First'.
Re: Jumbo Signatures extensions discussion
by tobyink (Canon) on Nov 29, 2019 at 10:48 UTC

    I have attempted to post the following reply to perl5-porters, but it doesn't seem to have appeared. (Spam trap? Moderation? I don't know. If it hasn't gone through in a few hours, I'll try responding again.) It's a response to the type constraint section.

    This is all pretty over-engineered. It can be simplified to something that would work well with existing type implementations such as MooseX::Types, MouseX::Types, Type::Tiny, and Specio. First, make this: sub f ($x is Int) { ...; } effectively a shorthand for: sub f { my ($x) = @_; Int->check($x) or Carp::croak(Int->get_message($x)); } Second, define these: use Scalar::Util; sub UNIVERSAL::check { my ($class, $object) = @_; blessed($object) && !blessed($class) && $object->DOES($class); } sub UNIVERSAL::get_message { my ($class, $value) = @_; "$value is not $class"; } Third, nothing. There is no third. Once you've got those two things working, then the following will "just work": use MooseX::Types::Common qw(PositiveInt); sub add_counts ($x is PositiveInt, $y is PositiveInt) { return $x + $y; } This works because PositiveInt is just a sub that returns an object with `check` and `get_message` methods. And this will also "just work" (thanks to UNIVERSAL): sub fetch_page ($ua is HTTP::Tiny, $url) { my $response = $ua->get($url); $response->{success} or die $response->{message}; return $response->{content}; } Yeah, `check` and `get_message` are pretty generic-sounding methods to be adding to UNIVERSAL and there are arguments in favour of, say, `CHECK` and `GET_MESSAGE`. The advantage of `check` and `get_message` is that the following already works out of the box in MooseX::Types, MouseX::Types, and Type::Tiny, and can be made to work with Specio with a pretty small shim. Int->check($x) or Carp::croak(Int->get_message($x)); Shim for Specio is: sub Int () { return t("Int") } > Also, the nested HashRef[ArrayRef[Int]] form quickly becomes a > performance nightmare, with every element of the AoH having to > be checked for Int-ness on every call to the function. It's not as bad for performance as you might think. MouseX::Types and Type::Tiny are capable of checking HashRef[ArrayRef[Int]] with a single XS sub call. MooseX::Types and Specio will check it without XS, but it's still a single sub call, just with a lot of loops and regexp checks. That said, there are performance improvements that can be made. One would be at compile time, when Perl sees: sub f ($x is Int) { ...; } It would call: $code = eval { Int->inline_check('$x') }; The Int object would return a string of Perl code like: q{ defined($x) && !ref($x) && $x =~ /^-?[0-9]+$/ } And this would be inlined into the function like: sub f { my ($x) = @_; do { defined($x) && !ref($x) && $x =~ /^-?[0-9]+$/ } or Carp::croak(Int->get_message($x)); } (Note I'm using the block form of eval when Perl fetches the inline code, so Perl isn't evaluating the string of code at run time. It just allows Int to throw an exception if it's unable to inline the check. Some checks are hard or impossible to inline.) Once again, there's discussion to be had about the name of the method `inline_check`, but Type::Tiny and Specio already offer an `inline_check` method exactly like this. And Moose offers `_inline_check`. Mouse offers neither, so Perl would just fall back to doing Int->check($x) to check the value. Coercions are a whole different kettle of fish and how they interact with aliasing and read only parameters can get confusing. For this reason, I'd recommend simply leaving them out of signatures, at least while people get used to having type constraints in signatures. People can coerce stuff manually in the body of their sub. This is not hard, it's probably what they're doing already, and it's almost certainly more readable than any syntax you can squeeze into the signature.
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://11109414]
Approved by haukex
Front-paged by Discipulus
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2021-12-04 23:05 GMT
Find Nodes?
    Voting Booth?
    R or B?

    Results (30 votes). Check out past polls.