Re: [PHP-DEV] [VOTE] Named arguments

This is only part of a thread. view whole thread
  110914
July 10, 2020 10:24 ocramius@gmail.com (Marco Pivetta)
Hey Rowan,
<http://ocramius.github.com/>


On Fri, Jul 10, 2020 at 12:11 PM Rowan Tommins collins@gmail.com>
wrote:

> On Fri, 10 Jul 2020 at 10:21, Marco Pivetta <ocramius@gmail.com> wrote: > > > I kept my "NO" stance here, as per discussion in > > https://externals.io/message/110004#110005, where I provided (in my > > opinion) good/safe alternatives to arrays as input parameters. > > > > > For the record, I maintain my reaction from > https://externals.io/message/110004#110010 that the example you give > "cheats" by including a "fromArray" method which simply emulates named > parameters, but without any assistance from the language would require a > large amount of boiler-plate to do so. >
If "array" is all you have to pass on as input, `fromArray` is what you can certainly use: that's useful when de-serializing from JSON input, DB, serialized state, etc. It certainly isn't meant to pass in the array via hand-crafted map: that's what other (additional) named ctors would be for.
> Would you be more open to a version of the feature that was opt-in at the > definition site?
Most certainly: as mentioned elsewhere (can't find it), having an attribute to demarcate whether an API can be used via named arguments would make the BC surface and BC promise explicit and clear.
> Then your example could swap the hand-rolled documentation > and validation in "fromArray" for a fully typed signature in > "fromNamedParams", and the only compatibility promises would be ones > already made by "fromArray" anyway.
The idea of multiple named ctors is to keep BC compatibility whilst allowing for multiple input formats that fit the current use-case. Data format changes? New ctor! Not a problem, unless internal invariants change massively. For example, in event-sourcing, you can likely never update past data (never, really!), so as you move forward, your de-serialization endpoints may be appropriately versioned: a `MyEvent::fromSerializedData(array)` must be designed to support all possible past representations of `MyEvent` (even if no longer complying with current domain rules), whereas `MyEvent::fromInput(PSR7Request)` can actively refuse invalid information (context matters a lot). ```php MyEvent::fromArray([ 'hand' => 'crafted', 'set' => 'of', 'parameters' => 'is', 'certainly' => 'not', 'how' => 'this', 'is' => supposed', 'to' => 'be', 'used', 'and' => 'causes', 'only' => 'trouble' ]); ``` The above is just complexity, and a lot of moving parts to keep track of. When coding with a defined data-set, you use `MyEvent::raise($with, $relevant, $typed, $information, $only);` I feel like I'm repeating myself though: this was already explained before. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/
  110915
July 10, 2020 12:56 rowan.collins@gmail.com (Rowan Tommins)
On Fri, 10 Jul 2020 at 11:24, Marco Pivetta <ocramius@gmail.com> wrote:

> If "array" is all you have to pass on as input, `fromArray` is what you > can certainly use: that's useful when de-serializing from JSON input, DB, > serialized state, etc. > > It certainly isn't meant to pass in the array via hand-crafted map: that's > what other (additional) named ctors would be for. >
As I said in my previous response, I think this comes down to an unfortunate example - if what you actually have is an array, then yes, fromArray makes sense. One of the use cases for named parameters happens to look superficially similar to that, because it can be worked around by first creating an array, and then passing it somewhere. But the input you actually have is not an array, it is a set of variables from multiple sources, or a set of hard-coded parameters of different types that configure the functionality. One aspect I'd like to highlight is that named parameters aren't just useful for large numbers of individual parameters, but large numbers of _valid combinations_. For instance, with two optional parameters, you can quite easily have named methods to omit one or the other: function fromFoo($foo) function fromBar($bar) function fromFooAndBar($foo, $bar) You could even skip the third method by having optional parameters on one or both of the other two. But this list would increase exponentially; with only three parameters, you would need: function fromFoo($foo) function fromBar($bar) function fromBaz($baz) function fromFooAndBar($foo, $bar) function fromFooAndBaz($foo, $baz) function fromBarAndBaz($bar, $baz) function fromFooAndBarAndBaz($foo, $bar, $baz) Right now, there is no elegant way to combine these into one function signature. Named parameters are one solution, because they let you specify defaults for all parameters: function fromFooAndOrBarAndOrBaz($foo=42, $bar=true, $baz=[]) Another solution is parameter skipping, which was declined largely because people preferred named params: https://wiki.php.net/rfc/skipparams The only other alternative I know of is the builder pattern ($builder->withFoo($foo)->withBar($bar)->withBaz($baz)->build()), which at least makes the boilerplate scale linearly rather than exponentially, but is still going to require dozens of lines of code to achieve what named parameters can do with one. Regards, -- Rowan Tommins [IMSoP]