|
| 1 | +# Named Parameters in Signatures |
| 2 | + |
| 3 | +## Preamble |
| 4 | + |
| 5 | + Author: Paul Evans <PEVANS> |
| 6 | + Sponsor: |
| 7 | + ID: TODO |
| 8 | + Status: Exploratory |
| 9 | + |
| 10 | +## Abstract |
| 11 | + |
| 12 | +Adds the ability for subroutine signatures to process named arguments in the form of name-value pairs passed by the caller, in a fashion familiar to existing uses of assignment into a hash. |
| 13 | + |
| 14 | +## Motivation |
| 15 | + |
| 16 | +Perl 5.20 added "subroutine signatures", native syntax for more succinctly expressing the common patterns of processing arguments inbound into a subroutine by unpacking the `@_` array. Rather than writing common styles of code directly, a more compact notation, in a style immediately familiar to users of many other languages, allows Perl to create the required behaviour from that specification. |
| 17 | + |
| 18 | +```perl |
| 19 | +sub f ($x, $y, $z = 123) { ... } |
| 20 | + |
| 21 | +# instead of |
| 22 | +sub f { |
| 23 | + die "Too many arguments" if @_ > 3; |
| 24 | + die "Too few arguments" if @_ < 2; |
| 25 | + my ($x, $y, $z) = @_; |
| 26 | + $z = 123 if @_ < 3; |
| 27 | + ... |
| 28 | +} |
| 29 | +``` |
| 30 | + |
| 31 | +One common form of argument processing involves passing an even-sized list of key/value pairs and assigning that into a hash within the subroutine, so that specifically named parameters can be extracted from it. There is currently no support from subroutine signature syntax to assist authors in providing such behaviours. |
| 32 | + |
| 33 | +## Rationale |
| 34 | + |
| 35 | +(explain why the (following) proposed solution will solve it) |
| 36 | + |
| 37 | +## Specification |
| 38 | + |
| 39 | +A new kind of element may be present in a subroutine signature, which consumes a named argument from the caller. These elements are written with a leading colon prefix (`:$name`), indicating that it is named rather than positional. The name of each parameter is implied by the name of the lexical into which it is assigned, minus the leading `$` sigil. |
| 40 | + |
| 41 | +Each element provides a new lexical variable that is visible during the body of the function, in the same manner as positional ones. |
| 42 | + |
| 43 | +The value of a named parameter is taken from the argument values passed by the caller, in a manner familiar to existing uses of hash assignment. The caller should pass an even-sized name-value pair list. The values corresponding to names of parameters will be assigned into the variables. The order in which the values are passed by the caller is not significant. |
| 44 | + |
| 45 | +Furthemore, all of the new behaviour is performed within the body of the invoked subroutine entirely by inspecting the values of the arguments that were passed. The subroutine is not made aware of how those values came to be passed in - whether from literal name-value syntax, a hash or array variable expansion, or any other expression yielding such a list of argument name and value pairs. |
| 46 | + |
| 47 | +```perl |
| 48 | +sub make_colour ( :$red, :$green, :$blue ) { ... } |
| 49 | + |
| 50 | +make_colour( red => 1.0, blue => 0.5, green => 0.2 ); |
| 51 | +# The body of the function will be invoked with |
| 52 | +# $red = 1.0 |
| 53 | +# $green = 0.2 |
| 54 | +# $blue = 0.5 |
| 55 | +``` |
| 56 | + |
| 57 | +As with positional parameters, a named parameter without a defaulting expression is mandatory, and an error will be raised as an exception if the caller fails to pass a corresponding value. A defaulting expression may be specified using any of the operators available to positional parameters - `=`, `//=` or `||=`. |
| 58 | + |
| 59 | +```perl |
| 60 | +sub make_colour ( :$red = 0, :$green = 0, :$blue = 0 ) { ... } |
| 61 | + |
| 62 | +make_colour( red => 1.0, blue => 0.5 ); |
| 63 | +# The body of the function will be invoked with |
| 64 | +# $red = 1.0 |
| 65 | +# $green = 0 |
| 66 | +# $blue = 0.5 |
| 67 | +``` |
| 68 | + |
| 69 | +A subroutine is permitted to use a combination of *mandatory* positional and named parameters in its definition, provided that all named parameters appear after all the positional ones. Any named parameters may be optional; there are no ordering constraints here. |
| 70 | + |
| 71 | +If a subroutine uses named parameters then it may optionally use a slurpy hash argument as its final element. In this case, the hash will receive all *other* name-value pairs passed by the caller, apart from those consumed by named parameters. If the subroutine does not use a slurpy argument, then it will be an error raised as an exception for there to be any remaining name-value pairs after processing. |
| 72 | + |
| 73 | +While all of the above has been specified in terms of subroutines, every point should also apply equally to the methods provided by the `class` feature, after first processing the implied invocant `$self` parameter. |
| 74 | + |
| 75 | +## Backwards Compatibility |
| 76 | + |
| 77 | +At the site of a subroutine's definition, this specification only uses new syntax in the form of the leading colon prefix on parameter names. Such syntax was formerly invalid in previous versions of perl. Thus there is no danger of previously-valid code being misinterpreted. |
| 78 | + |
| 79 | +All of the behaviour provided by this new syntax is compatible with and analogous to any existing code that could have been written prior, perhaps by direct assignment into a hash. There are no visible differences in the external interface to a subroutine using such syntax, and it remains callable in exactly the same way. Functions provided by modules could be upgraded to use the new syntax without any impact on existing code that invokes them. |
| 80 | + |
| 81 | +## Security Implications |
| 82 | + |
| 83 | +There are no anticipated security concerns with expanding the way that subroutines process parameters in this fashion. |
| 84 | + |
| 85 | +## Examples |
| 86 | + |
| 87 | +As the intention of this syntax addition is to make existing code practice more consise and simple to write, it would be illustrative to compare pairs of functions written in the newly proposed vs. the existing style. |
| 88 | + |
| 89 | +TODO |
| 90 | + |
| 91 | +## Prototype Implementation |
| 92 | + |
| 93 | +The [`XS::Parse::Sublike`](https://metacpan.org/pod/XS::Parse::Sublike) module contains parsing to allow third-party syntax modules to parse subroutine-like constructions, and includes a parser for named parameters already having this syntax. These are also made available to regular subroutines via the `extended` keyword provided by [`Sublike::Extended`](https://metacpan.org/pod/Sublike::Extended). |
| 94 | + |
| 95 | +## Future Scope |
| 96 | + |
| 97 | +* If the Metaprogramming API (PPC0022) gains introspection abilities to enquire about subroutine signature parameters, further consideration will need to be made in that API on how to represent the extra kinds of parameters added by this specification. |
| 98 | + |
| 99 | +## Rejected Ideas |
| 100 | + |
| 101 | +* This specification intentionally makes no changes to the call-site of any subroutines using named parameters. |
| 102 | + |
| 103 | +## Open Issues |
| 104 | + |
| 105 | +* How to specify a named parameter whose name is anything other than the name of the lexical variable into which its value is assigned? This is more of note when considering that traditional argument handling techniques involving assignment into hashes can handle more styles of name that would be invalid for lexical variables - for example, names including hyphens. |
| 106 | + |
| 107 | +## Copyright |
| 108 | + |
| 109 | +Copyright (C) 2024, Paul Evans. |
| 110 | + |
| 111 | +This document and code and documentation within it may be used, redistributed and/or modified under the same terms as Perl itself. |
0 commit comments