Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:114579 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 18791 invoked from network); 25 May 2021 10:20:03 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 25 May 2021 10:20:03 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 3989F1804D9 for ; Tue, 25 May 2021 03:31:17 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,FREEMAIL_REPLY, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-ed1-f48.google.com (mail-ed1-f48.google.com [209.85.208.48]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 25 May 2021 03:31:16 -0700 (PDT) Received: by mail-ed1-f48.google.com with SMTP id g7so23378168edm.4 for ; Tue, 25 May 2021 03:31:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=SyDA3OF6wPLUWWFn1XrxBMSV4I7K/eajZzwEnxsKpKs=; b=ZR0UpYcfnVqfc+/GmBRwJeBH+pE4u4MgE0OlD4fQ7npCvPnJACCVM4J3lM0hkNKM+l I01umzObp8w5KGkso1h+5/upSJ4DfJYst3Gc1HhZszpq2SeHSx9msu8ervK/zOuVr1c0 YlSbD7zCr1anDzIxHCTZQobEos0AGVWbgehWo1PEb+ZL7w9JQo8AegPKJKLvw7tWA30i yj5rUifgFpuQki2r2TCdrbbWoznbIUAvFyUanjFGGxDwP8A1pi56UFUN+/edct0ZaFyf YPYvyqonZcU+2xVA57r6+eUV7anzOB1t/7iDoKo08LF3UYZVPOCNr/cjUPIYFWrZKGTj Le0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=SyDA3OF6wPLUWWFn1XrxBMSV4I7K/eajZzwEnxsKpKs=; b=PaDhGHbGfOD5jUNOuN2Jh/QKySmhyqSjaqgV5vMOD7OFgejsKWy3lfzg0+kYSYWlSm WO8YyNyjYdg8ejvF39vQitZSb+ETkT1kpkDkuuwqaGs21XsFvRcO+idDmk2Wz7IxgmNu olpT0UsdcRD3rfrsDJwRPNUiV1XDF41EdsZrVgr2/NLgq7qmexBAqRsKbWp+mzB3qfms s/uIcnaYPU/EwpHEtAaMXY8bdAKjZ0Qh+8ygamwnJokyYQUY7kdjyVHS5GUDxjdNO6wp 0qLA5sDFSr3YVOo8QfnrIpLxCuqnYHiKe00H5hTrCE8UdpOmV5gPvTFPNflj8uIbl914 UosA== X-Gm-Message-State: AOAM531G10EuECc8lKxpVCCCtw+HTgkJ+UKHiCaf5zE0TbW/QPt39UTp ncThSX7T/bjGAs0bobn1aLjrRZtCykqSxEFW5WI= X-Google-Smtp-Source: ABdhPJziicRGq4kGtILAOzLAgqhtQTtn5FVIxf9qr5R7lHz/Vtct3rfFnHrhLI1Bq12AncCrCNSedv09mg0r3IF50r8= X-Received: by 2002:a50:fd11:: with SMTP id i17mr30632666eds.23.1621938673192; Tue, 25 May 2021 03:31:13 -0700 (PDT) MIME-Version: 1.0 References: <13cb59de-f335-4532-9e7a-400499661dbf@www.fastmail.com> In-Reply-To: <13cb59de-f335-4532-9e7a-400499661dbf@www.fastmail.com> Date: Tue, 25 May 2021 12:31:01 +0200 Message-ID: To: Larry Garfield , Nikita Popov Cc: php internals Content-Type: multipart/alternative; boundary="00000000000076936205c32506c6" Subject: Re: [PHP-DEV] [RFC] First-class callable syntax From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --00000000000076936205c32506c6 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Le ven. 21 mai 2021 =C3=A0 16:31, Larry Garfield a =C3=A9crit : > On Fri, May 21, 2021, at 2:52 AM, Nicolas Grekas wrote: > > Sorry for self-reply, this needs some clarifications :) > > > > Le ven. 21 mai 2021 =C3=A0 09:17, Nicolas Grekas < > nicolas.grekas+php@gmail.com> > > a =C3=A9crit : > > > > >> There's been a lot of rapid iteration, experimentation, and rejectio= n. > > >> The most recent alternatives are this one from Levi: > > >> > > >> https://gist.github.com/morrisonlevi/f7cf949c02f5b9653048e9c52dd3cbf= d > > >> > > >> And this one from me: > > >> > > >> https://gist.github.com/Crell/ead27e7319e41ba98b166aba89fcd7e8 > > >> > > >> The main takeaways (to give context to Nikita's proposal): > > >> > > >> * Because of optional arguments, using the same symbol for "copy one > > >> parameter from the underlying function" and "copy all remaining > parameters > > >> from the underlying function" is not viable. It runs into oddball > cases > > >> where you may intend to only use one argument but end up using > multiple, > > >> especially in array operation functions that sometimes silently pass > keys > > >> along to callbacks if they can. Hence the separate ? and ... that > were > > >> proposed. > > >> > > > > > > I've read that some ppl think this should be fought, but that's > actually a > > > critical feature of the engine when writing BC layers by adding extra > > > arguments via this possibility. > > > > > > > I'm talking about implicit extra arguments accessed via func_get_args() > > here. > > The issue is with optional arguments that would get auto-forwarded, even > when you don't want to use them. (It wasn't obvious to me for a long tim= e > that this issue existed either.) The classic example, ported from > Javascript: > > function printInt(string $value, int $base =3D 10): void > { > echo intval($value, $base), "\n"; > } > > $array =3D ['10', '10', '10', '10', '10']; > > array_walk($array, 'printInt'); > > Results in: > > 10 > 0 > 2 > 3 > 4 > > The function has multiple arguments, but we only want to use one, but > functions like array_walk() will try to use the optional one if they > can... even if doing so makes no logical sense. Having ? capture all > arguments unconditionally would also mean it captures optional arguments > even if you don't want it to. That's why we need to have the separate ..= . > symbol for when you really do mean "and all the rest". > Got it thanks. Makes sense to me now. In this case, we need printInt(?) to only pass $value through. If you > really did want to use the array key for the base for some reason, you > could do printInt(?, ?) or printInt(...). > > I think the plan would be that extra passed arguments to a partialed > function would still forward through, but that may end up being more > difficult than expected. > > > In the 2nd gist, I read "If the current position is a ..., copy all > > > remaining parameters from the underlying function, including a > variadic." > > > But to me it's important that all extra arguments are forwarded to th= e > > > partialized callable, where ... is used or not. > > > Please clarify this point if possible. > > I think this is the same as the previous question? > > (Note that several vocal people have argued that the way PHP passes along > extraneous arguments is a bug, not a feature. I don't have a strong > feeling about it, but some do.) > Being vocal doesn't mean being right :) This feature of the language is critical to write BC layers, see eg this, LSP rulez: https://github.com/symfony/symfony/blob/5.4/src/Symfony/Component/Form/Choi= ceList/Factory/ChoiceListFactoryInterface.php#L38 It has to stay (unless an alternative is provided of course.) > > * Named arguments make things more complicated. One of the questions i= s > > >> whether named placeholders should be supported or not. And if they > are, > > >> does that mean you can effectively reorder the arguments in the > partial > > >> application, and what does that mean for usability. It gets > complicated > > >> and scope-creepy fast. > > >> > > > > > > For the userland pov, the principle of least surprise would say we > should > > > answer yes to both questions. > > > > Reading your gists, I'm with drawing this expectation. > > > > Since calls reorder named arguments automatically, we could expect the > same > > for PFAs. > > That is: > > since foo(A: 1, B: 2) is exactly the same as foo(B: 2, A: 1) > > we could also expect that foo(A: ?, B: ?) would be exactly the same > > foo(B:?, A:?) > > > > The issue here is that the syntax can be interpreted as both a new > > signature and as a partial call. > > If we stay pure to the intend of PFAs, argument reordering shouldn't be > > allowed IMHO. Aka this should be interpreted only as a partial call. > > > > *If* we need a way to reorder arguments, I think it would be natural al= so > > to be able to *rename* arguments. > > Which is also where the rabbit hole gets extra deep, and why I am > perfectly happy to just disallow named placeholders. In the rare use cas= es > where you really do want/need to reorder arguments and do other esoteric > things, short lambdas already let you do that much more explicitly. That= 's > an edge case that already has a solution. We're focused here on making t= he > most common cases nicer. > > > That's where my syntax proposal based on short closures might be handy: > > > > fn($a, $b, ...) =3D> $callable($b, a: $a, ...) > > At that point we're not getting any benefit over just using arrow > functions. The point of PFA is that it's partially calling a function > as-is, in a compact way. That in PHP we'd be implementing it as > yet-another-closure object is an implementation detail. > And the point of PFA is also to copy the existing signature, which is a significant difference with short closures. This completely disqualifies my proposal :) But this also highlights a critical reason for adding PFAs: they're not only syntax sugar to write short closure, but an abstraction over the signature of partialized callables! Last proposal, just to be sure we screened them all: using "...?" instead of "..." $callable(...?) > > >> We also went down a rabbit hole of trying to make argument reorderin= g > > >> work because some people asked for it, but as noted that's a very de= ep > > >> rabbit hole. > > >> > > > > > > Then maybe named args should be forbidden for "?" placeholders. This > would > > > keep this possibility open of the future and close the "principle of > least > > > surprise" expectation if there are. Aka foo(bar: ?) should be a parse > error > > > for now at least. > > That is precisely the conclusion I reached and what my writeup says: The > only placeholders are positional placeholders. Named arguments are fine, > but not named placeholders. > > > > My own take is that the PFA discussion has been overly-bikeshedded, > which > > >> is unfortunate since I think we're quite close to now having a > workable > > >> answer that covers most reasonable use cases. While I agree Nikita'= s > RFC > > >> here would be an improvement over 8.0, I don't think throwing in the > towel > > >> on PFA yet is a good idea. It's a much more robust and powerful > approach > > >> that still gets us the "first class callable" syntax we all want (at > least > > >> I assume we all do), and lots of additional power to boot. I'd > rather see > > >> us try to drive PFA home to completion. If that proves impossible b= y > early > > >> July, then this RFC would still get us something this cycle, as long > as the > > >> syntax is still compatible with PFA. (Otherwise whenever PFA gets > sorted > > >> out in the future we end up with yet-more-ways to do the same thing > that > > >> are not optimizations of each other but just competing syntax, in > which > > >> case no one wins.) > > >> > > > > > > I agree. Let's figure out PFAs! > > > > > > If I may add to the bikeshedding, and since PFAs are like others said > > > "short lambas on steroids", what about borrowing the syntax from them= ? > > > > > > Here would be the first-class callable syntax: > > > > > > fn(...) =3D> $callable(...) > > > > > > and for PFAs: > > > > > > fn(...) =3D> $callable(?, 42, ...) > > > > > > Nicolas > > As above, not really viable. > > > Also, to answer Andreas as long as I'm here: Supporting "new Foo" with PF= A > is a separate question. As noted in the currently written draft of the > RFC, the problem is that object creation is not a function call; the obje= ct > is created, then the __construct initializer is called. A naive PFA > implementation would therefore create the object once, couple it to the > partial, and then calling the partial repeatedly would return the same > object, rather than repeatedly making a new object. Joe already figured > out how to special case that, though, so it's a non-issue. > > Both Levi's writeup and mine could support PFA constructors, AFAIK. > > --Larry Garfield > Nicolas --00000000000076936205c32506c6--