Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125293 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 170761A00BD for ; Mon, 26 Aug 2024 21:28:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1724707802; bh=4++28NoD7Nw8LbifBLnKUP0af8ngVuP4EGuykyAskbw=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=gw9QcreQyG9+3N8MRFulCWHIQRcMC2kAKlb3ZiOMWBdCkW48yB7EG52D0BeifYAxl ulwovSKkmN6aM2Yf1g9Z96s7Zs2iAtMt1OmRoUIWwWEBKNXCRMnNxlb10B6WyYph3L 2YP63cbslWU1oPXXskKBFH/4ezhRJNFyP23bqBqZQh0D7AnWo5/EIRS2iY3uqeluoC zTfXH7cLXt6KwmAPW91ZT50ES//4Ksdr8resKEIUhSntHrkrR0iU4yOkKpefEudgT3 wjdbdOWpyOLdiHtyD/E47P3WNs5oCcHXR7J2peG1IKfKsWppeOitfLW3bnEAHhO8Ha C5bsjocfOnpfw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id A1585180034 for ; Mon, 26 Aug 2024 21:30:01 +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.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-vk1-f173.google.com (mail-vk1-f173.google.com [209.85.221.173]) (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 ; Mon, 26 Aug 2024 21:30:01 +0000 (UTC) Received: by mail-vk1-f173.google.com with SMTP id 71dfb90a1353d-4feabcf2b10so1393934e0c.0 for ; Mon, 26 Aug 2024 14:28:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724707687; x=1725312487; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=oJ3+c3kWqbWVgWEnbpuHTZsdNROSKQqwe+KEUyM+i58=; b=Tt+Z0ciYL3FBTTF1G9EOlcoEcJQPGP8/nkum/5BmxO15kmk2hggUh2iDXiWrpS0eeE WfS/7jEcLZ0mwAYbv+FjqQ322s93j1Z4ZSofJfOUODa2JNzj6pYCmHrp039k5FnAL0yP Bm4vol9EiUrITpaGBtJc3CPQvuEYbf/NFdnuv75vYxshfCxYDecXp0Vpm7k42E79oWse Li4sQ4xymp5AkKOmA0jCRAwWxxB1zU5v1t47vIOPsaZ8HyUP78eI/NfyYBYzhpoR/QFs puMiOi9HdF6masO52usEVIstUivrCX+q6NHbl7Vgclef5Rw8wQcqssYhZ7E3sjlSV3aV Tyzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724707687; x=1725312487; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=oJ3+c3kWqbWVgWEnbpuHTZsdNROSKQqwe+KEUyM+i58=; b=o+d57nqIxkl4Y75EKHMqgBPdC1/NPwrKGz/zgRSQQB6lPYRqbSbTbJNG8kYji8BBLN B3DZleFOQOodQYj8Jd0TSktzK/uYWsujrnare7ty1QTHJg1ywKihJC0QOFXwJnA0MJI9 dPAVJqlP4Xg7zwPswmIKM+q32e8kdH0cb4n4QSuhbHjUzAN/Y0ZzRnBHIMUM/xwf5YfL TDhi2lGrrkEosI8DjK56nB/WIx2uFGKRmfCxKD7+jHJdSwNxuLLrUHMYpyhhrc856EtM tLrgoes2Wi0AYqPOMStOCsA2OZCdxVDa/8SlRSifB0lVNGJfIKpLq+k1H/xCJiPXBLX+ 6LLg== X-Forwarded-Encrypted: i=1; AJvYcCWoQrVdTQPQu8xmc1lsGDQemf9OAN6DykLS2LFWKeijTCFiVLFx21pOJSO3OGoSzMY9gqphVrvIofs=@lists.php.net X-Gm-Message-State: AOJu0YyjhwITykVTpeWXNrQ2Jad69ssYErJrlWFVv3bDxyt9p8myVsEh 941BddcwA3hifk6nlQEiEZVBbC3n0cdAMa+787GyCxQAuNQn9L2dHGSLOJE/gJ0J+W6+FufjK6b Og+eau/9y2z7Mje534DV0pKy+/aE= X-Google-Smtp-Source: AGHT+IHZa5PI6CeukxMn3LCzr6zR/yAcK07WNrkDfqlZUkpdX51h9UUu12UkRvSryL1K9DSGIkD5hKO6aCDm8LeFbx0= X-Received: by 2002:a05:6122:1309:b0:4f5:197a:d679 with SMTP id 71dfb90a1353d-4fed68151d3mr1240828e0c.1.1724707687154; Mon, 26 Aug 2024 14:28:07 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Mon, 26 Aug 2024 16:27:55 -0500 Message-ID: Subject: Re: [PHP-DEV] [RFC] Default expression To: John Coggeshall Cc: Larry Garfield , php internals Content-Type: multipart/alternative; boundary="00000000000008cedb06209ccd1d" From: mweierophinney@gmail.com ("Matthew Weier O'Phinney") --00000000000008cedb06209ccd1d Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Mon, Aug 26, 2024 at 3:45=E2=80=AFPM John Coggeshall wrote: > > > On Aug 26 2024, at 2:11 pm, Matthew Weier O'Phinney < > mweierophinney@gmail.com> wrote: > > > I can see a few others: > > - string concatenation. I might want to prepend or append a string to a > default. > > - fractional or multiplicative application, e.g. for durations/timeouts. > These might require testing for non-zero first as well. > > > I have to be honest all of these both sound like really bad practices. > Maybe I'm just not being imaginative enough... but if you don't control t= he > upstream library why would you ever trust it like that? Writing a timeout= default > * 0.5 when default is 1000 is a really bad way to set your timeout to > 500 -- because next time you done a composer update suddenly the > upstream package set the timeout to 5000 and now instead of 500 your > timeout has silently become 2500. > You'll likely identify the increased delay in such cases. Generally speaking these sorts of default values don't change a ton, but it's not unlikely that you may say "I'd like half that delay" or "twice that delay". But a better example would be introducing a backoff, which might look like this: for ($i =3D 1; $i +=3D 1 ; $i < 4) { $result =3D call_some_client($url, default * $i); if ($result->isSuccess()) { break; } } In other words, you know that you want it to use the default, and then allow an increasing timeout duration between calls if it fails. For this, I don't necessarily want or need to know what the default is, only that I want to do _multiples_ of it in specific cases. Is it a universally good idea? No. Does it have use cases? Yes. - decorating a default instance (e.g. to lazily create a proxy without > knowing the default implementation used for an argument hinted against an > interface) > > > This is exactly the usage I'm highlighted as problematic in my code > example. You're introducing a new worry for the upstream API developer th= at > doesn't need to exist, and violating a separation principle that has > existed in PHP since default parameters were created 25+ years ago. > How exactly is this worrisome? Consider this: class A { public function __construct(private LogInterface $logger =3D new DefaultLogger()) { } } class ProxiedLogger implements LogInterface { ... } $a =3D new A(new ProxyLogger(default)); If class A is programming to the `LogInterface` contract, the fact that it gets a proxied version of the default should not matter in the least. Being able to proxy like this means that a _consumer_ of class A does not need to know or care what the default implementation is; they can assume it follows the same contract, and proxy to it regardless. The upstream developer doesn't need to care, because they are programming to the interface, not the implementation. This doesn't violate the separation of concerns principle, nor covariance. > IF it's possible to accomplish, I think it's better to identify the > "leaving this open will create WTF situations" than to prematurely lock > _everything_ down up front. > > > If this feature is released with an overly broad scope in terms of > expressions, etc. it's not like we can take it back at that point because > now people are using it in unknown ways. It is not one I'm comfortable wi= th > a "let's move forward and see what happens" approach. > I didn't say that at all. I said we should identify the ones we absolutely know will be problematic, and restrict those from the outset. From there, we should identify the ones that _might_ be problematic, and determine on a case by case basis if the risks outweigh the use cases before Bilge brings it to a vote. But if we lock it down too tightly from the outset, expanding it, while being possible, will more than likely mean an RFC for every expansion, because it's unlikely somebody will do anything comprehensive towards opening it up in the future. I'd rather not leave some of these use cases as a TBD for a later RFC, because that's very likely going to mean "never". I DO think there are likely whole categories of expressions we can likely say "no" to - anything where the default represents a union type (and _mixed_ is a union type) should likely only allow default by itself or as a bare value on the RHS of an expression. The argument against the feature that it expands the public API is puzzling to me, particularly when the only other solutions are (a) Reflection, or (b) named arguments. Named arguments _are_ part of the public API, as the names themselves can change. Default values can change, but, importantly, a change in a default value does not change the actual signature of a function. Giving the ability to use `default` gives consumers of a function a far more stable API surface, particularly when they do not want to change a given default value, but _do_ want to provide a more specific value for a later argument. Right now, if not using named arguments (e.g., because you're worried the argument name could change), your only other option is to use the Reflection API, which is more expensive, introduces a whole set of other possible runtime issues, and is far more convoluted to achieve. Without this RFC, those are your only options. Had this been only to allow the `default` keyword, I don't think we'd be having this discussion at all; I think it would be pretty self-evident that there's a need, and a lot of folks would be happy to sign on. But the author took it a step further, and asked, "What if ...?", and as such, the RFC provides additional benefits beyond giving you a keyword for using the default value, as it expands to allowing expressions. This gives a tremendous amount of flexibility and power, and solves some additional issues some of us have noticed. So I'd argue that what we need to weigh now is which of these expressions are truly benefits, which ones are side effects we can live with, and which will raise new problems we do not want to deal with. Again, there are a few lists going around on this thread, and I hope that Bilge is taking notes to add to the RFC, and working with the folks who helped him with implementation to determine what is and is not possible in terms of the grammar so we can potentially exclude the more problematic ones. But let's not just say "no expressions" - I think there's been some demonstrated value from a lot of these. > > --=20 Matthew Weier O'Phinney mweierophinney@gmail.com https://mwop.net/ he/him --00000000000008cedb06209ccd1d Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
On Mon, Aug 26, 2024 at 3:45=E2=80=AF= PM John Coggeshall <john@coggesha= ll.org> wrote:


On Aug 26 2024, at 2:11 pm, Matthew Weier O'Phinney = <mweieroph= inney@gmail.com> wrote:

I = can see a few others:

- string concatenation. I mi= ght want to prepend or append a string to a default.=C2=A0

- fractional or multiplicative application, e.g. for durations/tim= eouts. These might require testing for non-zero first as well.
<= /blockquote>

I have to be honest all of these both sound like = really bad practices. Maybe I'm just not being imaginative enough... bu= t if you don't control the upstream library why would you ever trust it= like that? Writing a timeout default * 0.5=C2=A0 when d= efault=C2=A0 is 1000 =C2=A0is a really bad way to set y= our timeout to 500=C2=A0 -- because next time you done a composer update=C2=A0 suddenly the upstream package set the timeout= to 5000=C2=A0 and now instead of 500 your timeout has silentl= y become 2500.

You= 'll likely identify the increased delay in such cases. Generally speaki= ng these sorts of default values don't change a ton, but it's not u= nlikely that you may say "I'd like half that delay" or "= twice that delay". But a better example would be introducing a backoff= , which might look like this:

=C2=A0 =C2=A0 fo= r ($i =3D 1; $i=C2=A0+=3D 1 ; $i < 4) {
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 $result =3D call_some_client($url, default * $i);
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 if ($result->isSuccess()) {
=C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }=
=C2=A0 =C2=A0 }

In other words, you kno= w that you want it to use the default, and then allow an increasing timeout= duration between calls if it fails. For this, I don't necessarily want= or need to know what the default is, only that I want to do _multiples_ of= it in specific cases.

Is it a universally good id= ea? No. Does it have use cases? Yes.

- decorating a default in= stance (e.g. to lazily create a proxy without knowing the default implement= ation used for an argument hinted against an interface)

This is exactly the usage I'm highlighted as problematic in my code e= xample. You're introducing a new worry for the upstream API developer t= hat doesn't need to exist, and violating a separation principle that ha= s existed in PHP since default parameters were created 25+ years ago.
=

How exactly is this worrisome? Consi= der this:

=C2=A0 =C2=A0 class A {
=C2=A0= =C2=A0 =C2=A0 =C2=A0 public function __construct(private LogInterface $log= ger =3D new DefaultLogger()) { }
=C2=A0 =C2=A0 }

=C2=A0 =C2=A0 class ProxiedLogger implements LogInterface { ... }<= /div>

=C2=A0 =C2=A0 $a =3D new A(new ProxyLogger(default= ));

If class A is programming to the `LogInterface= ` contract, the fact that it gets a proxied version of the default should n= ot matter in the least. Being able to proxy like this means that a _consume= r_ of class A does not need to know or care what the default implementation= is; they can assume it follows the same contract, and proxy to it regardle= ss. The upstream developer doesn't need to care, because they are progr= amming to the interface, not the implementation. This doesn't violate t= he separation of concerns principle, nor covariance.
=C2=A0 =C2= =A0=C2=A0
IF it's possible to accomplish, I think it's better to ident= ify the "leaving this open will create WTF situations" than to pr= ematurely lock _everything_ down up front.=C2=A0

= If this feature is released with an overly broad scope in terms of expressi= ons, etc. it's not like we can take it back at that point because now p= eople are using it in unknown ways. It is not one I'm comfortable with = a "let's move forward and see what happens" approach.

I didn't say that at all. I = said we should identify the ones we absolutely know will be problematic, an= d restrict those from the outset. From there, we should identify the ones t= hat _might_ be problematic, and determine on a case by case basis if the ri= sks outweigh the use cases before Bilge brings it to a vote.

=
But if we lock it down too tightly from the outset, expanding it= , while being possible, will more than likely mean an RFC for every expansi= on, because it's unlikely somebody will do anything comprehensive towar= ds opening it up in the future. I'd rather not leave some of these use = cases as a TBD for a later RFC, because that's very likely going to mea= n "never".

I DO think there are likely w= hole categories of expressions we can likely say "no" to - anythi= ng where the default represents a union type (and _mixed_ is a union type) = should likely only allow default by itself or as a bare value on the RHS of= an expression.

The argument against the feature t= hat it expands the public API is puzzling to me, particularly when the only= other solutions are (a) Reflection, or (b) named arguments. Named argument= s _are_ part of the public API, as the names themselves can change. Default= values can change, but, importantly, a change in a default value does not = change the actual signature of a function. Giving the ability to use `defau= lt` gives consumers of a function a far more stable API surface, particular= ly when they do not want to change a given default value, but _do_ want to = provide a more specific value for a later argument. Right now, if not using= named arguments (e.g., because you're worried the argument name could = change), your only other option is to use the Reflection API, which is more= expensive, introduces a whole set of other possible runtime issues, and is= far more convoluted to achieve.

Without this = RFC, those are your only options.

Had this been on= ly to allow the `default` keyword, I don't think we'd be having thi= s discussion at all; I think it would be pretty self-evident that there'= ;s a need, and a lot of folks would be happy to sign on.

But the author took it a step further, and asked, "What if ...?= ", and as such, the RFC provides additional benefits beyond giving you= a keyword for using the default value, as it expands to allowing expressio= ns. This gives a tremendous amount of flexibility and power, and solves som= e additional issues some of us have noticed.

So I&= #39;d argue that what we need to weigh now is which of these expressions ar= e truly benefits, which ones are side effects we can live with, and which w= ill raise new problems we do not want to deal with.

Again, there are a few lists going around on this thread, and I hope that= Bilge is taking notes to add to the RFC, and working with the folks who he= lped him with implementation to determine what is and is not possible in te= rms of the grammar so we can potentially exclude the more problematic ones.=

But let's not just say "no expressions&q= uot; - I think there's been some demonstrated value from a lot of these= .



--
Matthew Weier O'Phinney
mweierophinney@gmail.com<= br>https://mwop.net/
he/him
--00000000000008cedb06209ccd1d--