Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124637 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 5EB621A00B7 for ; Sat, 27 Jul 2024 07:20:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1722064917; bh=XVBFLkP0dT8uqUFY/A/sZPCLZf059ehqKCE0gsIhTQY=; h=In-Reply-To:References:Date:From:To:Cc:Subject:From; b=LfKh3jg0HM47VClbWUFQKrSPws/uNcbZbX8PW3nu8oUVTaB8o4Oa2aUFACoSKEMfn jDWFu3KuagNqKCjzVenQ0XmkUaRj/c90CTZ5Rd7Lc1aiC210XwPNjdbtEvXkfxCZR6 X/tbBLOTOlaoHx+FKOnpDAr52DmpdMRu8y0yb5KsGmNI8e+rpS1MULn7wE9mUFGBjT XM0BjI9QzD4EtYVGI7IBVU+nW4Ps2M3Le8xGsGxQTIOI4qxFkNzMBnj97NIpBUfPy/ /hDU8Ng/aAPtm6YINwXaxdp3DBlhaD43i/7SPQ9ViWv0r8gjJsFdUxrjJw3iEl6kCf qtIxcAk2stC3Q== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 4A70418003E for ; Sat, 27 Jul 2024 07:21:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout1-smtp.messagingengine.com (fout1-smtp.messagingengine.com [103.168.172.144]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sat, 27 Jul 2024 07:21:55 +0000 (UTC) Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailfout.nyi.internal (Postfix) with ESMTP id 98E7013805D8; Sat, 27 Jul 2024 03:20:18 -0400 (EDT) Received: from imap49 ([10.202.2.99]) by compute3.internal (MEProxy); Sat, 27 Jul 2024 03:20:18 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm1; t=1722064818; x= 1722151218; bh=CApr1ccwANjx0oeoyZdS8yV+jQqcvj4nTi2yyWRv+vU=; b=c nKJ3R1YsrYi/f8scAOKf4SwQxiDeEJraIU40lLH6fZuUlZNHUnxqza2R/JbQQiKh KiuXTlz+amVXXctd91fhAc4UF8dz5psDWD5cKOtDXj/nEUoujheGu29/CVvsBMjW kZEGJ71vjMo8Ag3f8XQE78WiCYTYs74iGLiJSq46lNY6fjlZUWOqZ9BGfNTorKJX Kmc+7Fa0WXTQd5uJfnWve4he3PMvj93N7j4JZl09+YFf12hVLDZMZNStP3gmZjdc obYV04g7OsiAnUz/u+ykaFr/18CU5VTS3plXb7/0oEJHXAtyHdHxgYBFnD9gTN8I 1Oyi/4JIbhUUwWnMP5uuw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm3; t=1722064818; x=1722151218; bh=CApr1ccwANjx0oeoyZdS8yV+jQqc vj4nTi2yyWRv+vU=; b=uJFOW3BzbE+NlBTr9USP9dUxCdVLyKVbla6wDKKOn7Fd 7gk98fkiSUnt+XB3SCOlU8rYYZsfZ8vaLCRc0MiD1h+gvK0IcrP4j7QU2VOYukz/ e2K90onrC9FrFcN4t2jvXFod357ZPC2zrJuuNjBfRVCRxGzUOWeFSBwdFGmgqaP9 /M891wYybzNX9NygPi9z8utaUw2vsATdGub9Gqj04YaH8Fte10kyc/xusWw+ON5/ qhADmQhSO4DSwsVpGiWtqRClnAK7sEo7Vz1HSs7koZHStb77rUN53AFMn9/QUf8J VJ4vkAFbnfHmCL71dI4h+lrhcChjd4GpnJHk+DoR3A== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeftddrieeigdduvdduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepofgfggfkjghffffhvfevufgtsegrtderreerreejnecuhfhrohhmpedftfho sgcunfgrnhguvghrshdfuceorhhosgessghothhtlhgvugdrtghouggvsheqnecuggftrf grthhtvghrnhepgfdvfeejfedvvdetieegkefhveejieffuedutedttefhtdeifeegtefh vdevvefgnecuffhomhgrihhnpehphhhprdhnvghtnecuvehluhhsthgvrhfuihiivgeptd enucfrrghrrghmpehmrghilhhfrhhomheprhhosgessghothhtlhgvugdrtghouggvshdp nhgspghrtghpthhtoheptd X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id 270DA15A0092; Sat, 27 Jul 2024 03:20:18 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.11.0-alpha0-582-g5a02f8850-fm-20240719.002-g5a02f885 Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Message-ID: <0042e36e-45c2-4e83-8eec-acde37b97d5a@app.fastmail.com> In-Reply-To: <34BF3A40-592C-41D7-8C35-5A9F3934E919@newclarity.net> References: <8ac3f087-a739-4759-891c-3dd19260ef5e@gmx.de> <34BF3A40-592C-41D7-8C35-5A9F3934E919@newclarity.net> Date: Sat, 27 Jul 2024 09:19:10 +0200 To: "Mike Schinkel" , "Christoph M. Becker" Cc: Bilge , internals@lists.php.net Subject: Re: [PHP-DEV] Explicit callee defaults Content-Type: multipart/alternative; boundary=e81d18eb77854da19a9c7d3f687274ff From: rob@bottled.codes ("Rob Landers") --e81d18eb77854da19a9c7d3f687274ff Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sat, Jul 27, 2024, at 02:04, Mike Schinkel wrote: > > On Jul 26, 2024, at 6:42 PM, Christoph M. Becker = wrote: > >=20 > > I have only skimmed your suggestion, but it sounds quite similar to > > . >=20 > I would really love to hear from some of those who voted "no" ~9 years= why they did so, and if they still feel the same. >=20 > > On Jul 26, 2024, at 5:54 PM, Bilge wrote: > > When writing a function, we can specify defaults for its parameters,= and when calling a function we can leverage those defaults implicitly b= y not specifying those arguments or by "jumping over" some of them using= named parameters. However, we cannot explicitly use the defaults. But w= hy would we want to? > >=20 > > Sometimes we want to effectively inherit the defaults of a function = we're essentially just proxying. One way to do that is copy and paste th= e entire method signature, but if the defaults of the proxied method cha= nge, we're now overriding them with our own, which is not what we wanted= to do. It is possible, in a roundabout way, to avoid specifying the opt= ional parameters by filtering them out (as shown in the next example). T= he final possibility is to use reflection and literally query the defaul= t value for each optional parameter, which is the most awkward and verbo= se way to inherit defaults. > >=20 > > Let's use a concrete example for clarity. > >=20 > > function query(string $sql, int $limit =3D PHP_INT_MAX, int $offset = =3D 0); > >=20 > > function myQuery(string $sql, ?int $limit =3D null, ?int $offset =3D= null) { > > query(...array_filter(func_get_args(), fn ($arg) =3D> $arg !=3D=3D= null)); > > } > >=20 >=20 > Yes, I run into that issue all the time. So much so that I adopted an= "optional args" pattern which itself has many downsides but in my code = it has had fewer downsides than doing it other ways. >=20 > So for your example I would write it like this (though I hate having t= o double-quote the names of the optional args): > function query(string $sql, array $args =3D []); >=20 > function myQuery(string $sql, array $args =3D []) { > query($sql, $args); > } >=20 > myQuery("SELECT * FROM table;", ["offset" =3D> 100])); >=20 > This is very flexible and a pattern that served me well for 10+ years.=20 >=20 > But of course this has none of the type safety nor code-completion ben= efits of named parameters, which I would sorely like to have. >=20 >=20 > > In this way we are able to filter out the null arguments to inherit = the callee defaults, but this code is quite ugly. Moreover, it makes the= (sometimes invalid) assumption that we're able to use `null` for all th= e optional arguments. > >=20 > > In my new proposal for explicit callee defaults, it would be possibl= e to use the `default` keyword to expressly use the default value of the= callee in that argument position. For example, the above implementation= for myQuery() could be simplified to the following. > >=20 > > function myQuery(string $sql, ?int $limit =3D null, ?int $offset =3D= null) { > > query($sql, $limit ?? default, $offset ?? default); > > } > >=20 > > Furthermore, it would also be possible to "jump over" optional param= eters without using named parameters. > >=20 >=20 > To me, while nice I feel it is very much like only a quarter step in t= he right direction. >=20 > I do not meaning to highjack your thread; feel free to ignore this if = you feel committed to pursing only your stated approach and are not inte= rested in what I would like to see. But I mention in hopes you and othe= rs see value in a more complete approach: >=20 > To begin, we can already currently do this (although I do not see many= people doing it in PHP; fyi this approach is the norm for many Go progr= ammers): >=20 > class QueryArgs { > public function __construct( > public ?int $limit =3D 100, > public ?int $offset =3D 0, > ){} > } >=20 > function myQuery(string $sql, QueryArgs $args =3D new QueryArgs()) { > query($sql, $args); > } >=20 > myQuery("SELECT * FROM table;", new QueryArgs(offset:100)); >=20 > I can only give my reasons for not doing it myself in PHP and speculat= e that others may have similar reasons and/or have never even considered= it: >=20 > 1. Not having an easy and consistent way to declare the "args" class = in the same file where the functions that use it are declared which keep= s it out-of-sight and makes it harder to keep updated than it needs to b= e. There=E2=80=99s nothing stopping you from doing that, except your autolo= ader. If you wanted to have every class ending with Arg load the same cl= ass without arg; so QueryArg and Query are usually in the same file (but= can be in separate files too), you could do something like this:

=
On Sat, Jul 27, 2024, at 02:04, Mike Schinkel wrote:
<= /div>
> On Jul 26,= 2024, at 6:42 PM, Christoph M. Becker <cmbecker69@gmx.de> wrote:

> I have only skimmed your suggestion, but it sounds quite si= milar to

<= /div>
I would really love to hear from some of those who voted "no" = ~9 years why they did so, and if they still feel the same.

> On Jul 26, 2024, at 5:54 PM, Bilge <bilge@scriptfusion.com> wrote:
<= /div>
> When writing a function, we can specify defaults for its = parameters, and when calling a function we can leverage those defaults i= mplicitly by not specifying those arguments or by "jumping over" some of= them using named parameters. However, we cannot explicitly use the defa= ults. But why would we want to?

&= gt; Sometimes we want to effectively inherit the defaults of a function = we're essentially just proxying. One way to do that is copy and paste th= e entire method signature, but if the defaults of the proxied method cha= nge, we're now overriding them with our own, which is not what we wanted= to do. It is possible, in a roundabout way, to avoid specifying the opt= ional parameters by filtering them out (as shown in the next example). T= he final possibility is to use reflection and literally query the defaul= t value for each optional parameter, which is the most awkward and verbo= se way to inherit defaults.

> = Let's use a concrete example for clarity.

> function query(string $sql, int $limit =3D PHP_INT_MAX, in= t $offset =3D 0);

> function m= yQuery(string $sql, ?int $limit =3D null, ?int $offset =3D null) {
>     query(...array_filter(func_get_arg= s(), fn ($arg) =3D> $arg !=3D=3D null));
> }


Yes, I run into that issu= e all the time.  So much so that I adopted an "optional args" patte= rn which itself has many downsides but in my code it has had fewer downs= ides than doing it other ways.

So for your = example I would write it like this (though I hate having to double-quote= the names of the optional args):
function query(string $s= ql, array $args =3D []);

function myQuery(s= tring $sql, array $args =3D []) {
    query= ($sql, $args);
}

myQuery("SEL= ECT * FROM table;", ["offset" =3D> 100]));

This is very flexible and a pattern that served me well for 10+ years= . 

But of course this has none of the = type safety nor code-completion benefits of named parameters, which I wo= uld sorely like to have.


>= ; In this way we are able to filter out the null arguments to inherit th= e callee defaults, but this code is quite ugly. Moreover, it makes the (= sometimes invalid) assumption that we're able to use `null` for all the = optional arguments.

> In my ne= w proposal for explicit callee defaults, it would be possible to use the= `default` keyword to expressly use the default value of the callee in t= hat argument position. For example, the above implementation for myQuery= () could be simplified to the following.

> function myQuery(string $sql, ?int $limit =3D null, ?int $o= ffset =3D null) {
>     query($sql,= $limit ?? default, $offset ?? default);
> }
<= div>> 
> Furthermore, it would also be possible= to "jump over" optional parameters without using named parameters.
<= /div>


To me, while nice I fe= el it is very much like only a quarter step in the right direction.
<= /div>

I do not meaning to highjack your thread; feel = free to ignore this if you feel committed to pursing only your stated ap= proach and are not interested in what I would like to see.  But I m= ention in hopes you and others see value in a more complete approach:

To begin, we can already currently do this (a= lthough I do not see many people doing it in PHP; fyi this approach is t= he norm for many Go programmers):

class Que= ryArgs {
   public function __construct(
     public ?int $limit =3D 100,
     public ?int $offset =3D 0,
&nbs= p;  ){}
}

function myQue= ry(string $sql, QueryArgs $args =3D new QueryArgs()) {
&nb= sp;  query($sql, $args);
}

myQuery("SELECT * FROM table;", new QueryArgs(offset:100));
=

I can only give my reasons for not doing it myself i= n PHP and speculate that others may have similar reasons and/or have nev= er even considered it:

1.  Not having = an easy and consistent way to declare the "args" class in the same file = where the functions that use it are declared which keeps it out-of-sight= and makes it harder to keep updated than it needs to be.

There=E2=80=99s nothing stopping you from do= ing that, except your autoloader. If you wanted to have every class endi= ng with Arg load the same class without arg; so QueryArg and Query are u= sually in the same file (but can be in separate files too), you could do= something like this:

<?php
function customArgAutoloader($className) {
  &= nbsp; if (substr($className, -3) =3D=3D=3D 'Arg') {
 =        $baseClass =3D substr($className, 0= , -3);
        $filePat= h =3D __DIR__ . '/' . $baseClass . '.php';

=         if (file_exists($filePath)) {=
         &nb= sp;  require_once $filePath;
    =     }
    }
}=

spl_autoload_register('customArgAutoloader= ');

(Untested, but you get the idea)



=E2=80=94 Rob
--e81d18eb77854da19a9c7d3f687274ff--