Hi Rowan,

> On May 5, 2020, at 16:47, Rowan Tommins <rowan.coll...@gmail.com> wrote:
> 
> Hi John,
> 
> On 05/05/2020 18:27, John Bafford wrote:
>> I very much do like the idea of named parameters, but I do not like the 
>> specific proposal in the linked RFC at all. I think that treating named 
>> parameters simply as syntactic sugar is not a good approach. If we're going 
>> to add named parameters, I think they should actually be significant in 
>> their own right. As such, please allow me to present an alternative.
>> 
>> I would propose the following opt-in mechanism, which would work by treating 
>> the parameter names as*part of the function name*. Doing it this way 
>> I_think_  should allow for no backwards compatibility issues with existing 
>> code and would especially give library authors full control over the naming 
>> and presentation of their APIs.
> 
> 
> I really like *some* of this proposal, but I think some of it is solving a 
> different problem.
> 
> 
> You mention in a later e-mail that you've been heavily inspired by Swift; 
> Swift in turn was inspired by Objective C, which was inspired by Smalltalk. 
> This is an important piece of history, because Smalltalk doesn't actually 
> have methods with named parameters; it has "keyword messages" whose 
> "selectors" are things like "indexOf:startingAt:". Swift makes them look more 
> familiar by keeping part of the name separate from what it calls "argument 
> labels", so a method might be called "indexOf(_:startingAt:)" or 
> "index(of:startingAt:)".

You're not wrong here, but, I think that's an (critical) implementation detail 
of Objective-C and Smalltalk that is not relevant here. Also, Swift does not 
use selectors or message passing, unless either interoperating with ObjC 
classes, or the code has explicitly opted-in. Normally, Swift function/method 
calls are statically determined at compile time in a way similar to C/C++.


> Although at a glance these look like named parameters, and they solve some of 
> the same problems, they're actually a fundamentally different concept.
> 
> They have some nice features:
> 
> * Method calls can be made to read like sentences
> * The external API of the function is kept separate from the internal 
> implementation
> * You can skip over default arguments by omitting their labels
> * Overloading what looks like one method, by having different methods whose 
> names start the same but have different "argument labels", may be appealing
> 
> But there are some crucial limitations:

I don't think any of your points are downsides to the proposal. A lot of these 
I think boil down to the conflict between, "as an API user, I should be able to 
do whatever I want", and, "as an API author, I have decided my API will be used 
like this". I generally side with the API author being specific on how their 
API will be used.


> * You can't call a method without using its labels; you'd need to have a 
> separate method with a similar name and no labels

If a function/method has labels, you're required to use them. Otherwise, you 
can't get the benefit of using them for name-based "overloading". For example, 
if you have the methods

        function firstIndex(of: $element) : ?int
        function firstIndex(where: callable $predicate) : ?int

You can't just call $collection->firstIndex($foo), because the compiler would 
have no idea which method it refers to. And while you _could_ have a function 
firstIndex($param) that tries to determine which specific method to call, but 
now you've turned a compile-time static call into a runtime check, and that 
might still not be sufficient, for example, if you have a collection of 
callables!

The labels are part of the method's name, which happen to be written inside the 
parenthesis. I just argue that both of those are more readable than either of 
these:

        function firstIndexOfElement($elt) : ?int
        function firstIndexMatchingPredicate(callable $predicate) : ?int

If I give my method a particular name, I expect users to call them as such. If 
the user doesn't like it, then they can wrap the class and provide their own 
alternate interface.


> * Similarly, it's up to the method's author to have versions with a mixture 
> of unlabelled and labelled parameters, so a call like "create('hello', 42, 
> locked: true)" is only possible if that specific variant is defined

As above: a method's author is under no compulsion to do so. If I define my API 
to be create('hello', fontSize: 42, locked: true), then that's how I expect my 
users to call it.


> * You can't use the labels in the "wrong" order, you have to know the order 
> they come in; so it doesn't solve the haystack/needle problem, except by 
> creating extra function definitions

I don't think this is a problem either. In some cases, the extra context by 
having the parameter names spell out a gramatically-correct sentence structure 
will help with memorizing the order. In other cases, the constant repetition 
will help. If you visually see strpos(haystack: $str, needle: $str) all over 
the place, it will help; but this can also be used as a way to fix the problem 
by completely sidestepping it, by adding new functions/methods that offer more 
clarity:

        strpos($string, in: $haystack);
        position(of: $string, in: $haystack)
        indexOf($str, in: $haystack)

In those examples, the sentence-like structure of the call, which are all 
variations on, "what is the position of $string in $haystack?" makes it more 
memorable.


> * Similarly, methods with large numbers of arguments are still hard to use 
> because you need to know both the name *and* the relative position of the 
> arguments you're providing (imagine defining variants with every order of the 
> 5 bit flags in the AMQP example in my earlier e-mail)

Well, you wouldn't define variants for each order, you'd just rely on the 
compiler (or IDE) to help the user. So if I tried to call

        $channel->queue_declare('name', arguments: $args, durable: true)

I'd expect the compiler to report back something along the lines of, 
"parameters are in the wrong order; use queue_declare(_: passive: durable: 
exclusive: autodelete: nowait: arguments: ticket:)". Maybe even prefix or 
postfix the parameter names with a ? to indicate the ones that are optional 
because there's a default value.

Yes, you _could_ add additional function definitions to handle whatever random 
order people want to use functions in, but you don't have to, and most API 
authors probably wouldn't. Because there's generally only a few "correct" ways 
to word most sentences. In that regard, AMQP::queue_declare is a pathological 
exception. There may be a good reason for its specific order, but lacking that 
knowledge, it appears to be completely arbitrary and nonintuitive.

If I said to someone, "language the hard improve working We constantly PHP to 
are", they'd probably look at me funny. We don't accept people speaking human 
language with randomized word order; neither should API authors (or other 
developers) be expected to accept people using APIs with randomized parameter 
orders.


> It would be possible to take the "outside name is different from inside name" 
> concept and apply it to named parameters proper. But the problems I would 
> like to solve with named parameters wouldn't be solved by Swift/Smalltalk 
> style functions.
> 
> 
> Regards,
> 
> -- 
> Rowan Tommins (né Collins)
> [IMSoP]

-John
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to