Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125264 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 911781A00BD for <internals@lists.php.net>; Mon, 26 Aug 2024 10:43:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1724669149; bh=y/DC0iOY4BNpEd8Nd5SZc/eyj769fRQtoWUlxbsUJEg=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=DUdRaijXqqiFfg98uaZXzWEMMFhw8rNBmqUi/GjIO8Z9k59Jis4MzFv4AMnVmsYBm B94S4dkPTX1FfY5baJH4wiwW0/7fTKUTRRoSfRkNBNHg2HhBVG9JCRC9g+UkeQBrlc RZ/ckSKT9tQYsOpHOyqqu6ywTQNiCC/dSMTRe8b/V2Gv2WW3H+mrOilC7YCzC8fKC/ BBBu8nZ5+g2l2Immt78zYz4r/3UQNFqn5FvQ2MuFDavXhW+R47YOEKNlKOwC8KpoM5 c07Y4eMn/p+s0YtzceCRdhB8xXMgFD9G4nKxsG8I7L7jAuVQjhoLyzXqDRvy6dsXfD Axm20eq/eoq6w== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id B322B1801E3 for <internals@lists.php.net>; Mon, 26 Aug 2024 10:45:45 +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.8 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DMARC_MISSING,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: <mike@newclarity.net> Received: from mail-yw1-f172.google.com (mail-yw1-f172.google.com [209.85.128.172]) (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 <internals@lists.php.net>; Mon, 26 Aug 2024 10:45:43 +0000 (UTC) Received: by mail-yw1-f172.google.com with SMTP id 00721157ae682-6b59a67ba12so37888477b3.1 for <internals@lists.php.net>; Mon, 26 Aug 2024 03:43:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20230601.gappssmtp.com; s=20230601; t=1724669030; x=1725273830; darn=lists.php.net; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=RFLLeY+ubJNOMz083yas/4CxwHBrTxtECCmEwXVWds4=; b=RaN8X5qk41FS79Yf2a5yUYEn9aSiDUZWd6du5IOi5FdwDkmRRDLKx/X3tnLxpWNwRb bS5QJzD9khnvsYgrN8YO4BMX/SuS5qOXZ0QobTpykxjMQeuB7i7Za9hq1oq2vV5xlCJU 8s6IYtmWXoo6VHuNyvTsCFodoma8K+ZLYJ83XYWdVhQc2sBoPhbRY3EYWhIyJJUfvF7Z 6t8Mn1BhnevvQt9zI/GEBQUGeWnrMlaf05GNmLx1X4+Mah1+uc7UbKI553lVoEJvTfA1 ZX2ZF3ISUeMRH6eWFfLC8Ed1Cm5h0xSbOuG7w32VQzd0Mx/Pp2IPJCcqCqbivdje7ZJW UZwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724669030; x=1725273830; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=RFLLeY+ubJNOMz083yas/4CxwHBrTxtECCmEwXVWds4=; b=BH/rNlO0zmOr9WqwOzKm0D51/5HqWYFyhTDqhPTDUVehVXpdhBG+AS74w0u/1azKl1 LxZGPAQKBDDEwrfnhEcs02FIv6pEb5kCRJZwULrQYk3FKfjf4pdBIhuo3Tt9+Yh61aHP zbeiI9MVoadshWwvBGYG1LP6AWyMkpM5HLl6vANbKTs9FS0M1442zHRKQNqGCQXWcPK8 UNmbsx6V0ngJnrcpUsd8lpl0pR8SbP2EqWer+cgr/Dmg3L063RMAsy9TXGi0jwn03yhi kU8ne18gwi59ZrARNvynBjrZzWxLN3WPRaIR5CIhhVf3OVGrB0DD29G5zoJLk5xFeqY/ 3o5Q== X-Gm-Message-State: AOJu0YzAygqnUMbRj7S7kxjq4vvgZJk9OkvWENl5/kXoHo4omeDrMZo1 aZ7g6SHrgpI1IlsZv/EBNMZNsDbz52h4brOP1x4yb/SJ7c/tS0iuSdM/X7v8hG3GF0M13Qg7XiA eX4w= X-Google-Smtp-Source: AGHT+IGKm2EP1iG8Yuu8hSIHfglTadnR6DU08ceZ3Fi7WLEBXcXg0eLLGo7f/kQjsntlfohqkGUxgQ== X-Received: by 2002:a05:690c:6608:b0:64a:9832:48a with SMTP id 00721157ae682-6c61f3a5766mr136088827b3.0.1724669029768; Mon, 26 Aug 2024 03:43:49 -0700 (PDT) Received: from smtpclient.apple (c-98-252-216-111.hsd1.ga.comcast.net. [98.252.216.111]) by smtp.gmail.com with ESMTPSA id 00721157ae682-6c399cb5435sm14880237b3.2.2024.08.26.03.43.49 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 26 Aug 2024 03:43:49 -0700 (PDT) Content-Type: text/plain; charset=utf-8 Precedence: bulk list-help: <mailto:internals+help@lists.php.net list-unsubscribe: <mailto:internals+unsubscribe@lists.php.net> list-post: <mailto:internals@lists.php.net> List-Id: internals.lists.php.net x-ms-reactions: disallow Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3696.120.41.1.10\)) Subject: Re: [PHP-DEV] [RFC] Default expression In-Reply-To: <0B974990-05FE-4C46-9C2B-3C243C7E8E81@getmailspring.com> Date: Mon, 26 Aug 2024 06:43:48 -0400 Cc: "Rowan Tommins [IMSoP]" <imsop.php@rwec.co.uk>, Andreas Leathley <a.leathley@gmx.net>, Andreas Heigl <andreas@heigl.org>, John Coggeshall <john@coggeshall.org> Content-Transfer-Encoding: quoted-printable Message-ID: <07E4ABD5-B9CE-47FA-BD9E-E42A82CFC737@newclarity.net> References: <99846F78-38F8-4549-B0A6-6310933AA78D@newclarity.net> <0B974990-05FE-4C46-9C2B-3C243C7E8E81@getmailspring.com> To: "internals@lists.php.net" <internals@lists.php.net> X-Mailer: Apple Mail (2.3696.120.41.1.10) From: mike@newclarity.net (Mike Schinkel) > On Aug 26, 2024, at 2:26 AM, John Coggeshall <john@coggeshall.org> = wrote: > The proposal in the RFC creates a new dependency and backward = compatibility issue for API developers that currently does not exist. It = is not just because it allows for non-sensical expressions, but that it = allows perfectly sensical expressions that would create dependencies = between libraries that I don't think are a worthwhile tradeoff. See my = code example below. If you reread my email you'll note I divided it into two objections and = your reply seems not to recognize that.=20 Your comment defends against the objection I listed as #2 but you quoted = the discussion of the objective listed as #1.=20 > (TL;DR; Down the thread a bit I put together a concrete example of why = I'm opposed to this RFC) I read through the entire thread and I did not see any examples you = provided that provided any real-world concerns, unless I misunderstood. = But since you provided a better example let us just ignore my comments = on those. > Consider this metaphor -- If I have a object with private properties, = PHP doesn't allow me to reach into that object and extract those values = without a lot of work (e.g. Reflection) by design. Right now, there are = lots of libraries out there defining default values and right now today = they are in the same sense "private" to the function/method they are = defined for. This PR would change the visibility of those default = values, pulling them higher up into the call stack where IMO they don't = belong -- essentially making them a brand new dependency API devs need = to worry about. I acknowledged that. However, I was not convinced that the default value = concern is an actual concern vs. a theoretical concern. So that is why = I asked for specific examples where it would cause a real problem vs. = just a theoretical concern.=20 > Sure if I have `foo(int $index=3D1)`, a developer calls with = `foo(default*3)`, and then the author of foo changes the signature to = `foo(int $index=3D0)` that might cause problems, but what is a real = world scenario where a developer would actually do that, the author then = change it, and then is causes a non-trivial problem? >=20 > How is that not a real-world contrived scenario? Because there was no use-case described for: 1. The purpose of the function, 2. Why someone would multiply the default times three, nor 3. Why the API developer would break BC and change the default for the = use-case.=20 That kind of information is what I was looking for. Besides, are you really calling a function named "foo()" a "real-world = scenario?" ;-) Anyway, I am going to skip to your example because otherwise I would = just be repeating my call for concrete examples in response to your = earlier comments in that reply. > Let me propose this example and see if you still hold firm to your = option that the following expression would not be valid and that it = still would not be a good idea for the language: > class MyDependency {...} > function doSomething(MyDependency $dep=3D new MyDependency) {...} > doSomething((default)->WithLogger(new Logger)); >=20 > Let's make that a little more complicated so you'll see the problem -- = Consider this rather lengthy example building off the concept of your = example: >=20 > <snip> >=20 > (new A)->withLogger((default)->log('B')); Well, this is not the example I asked you to comment on. Yes, you are = using `WithLogger()` but you are not using it in the same context I = asked about.=20 Nonetheless, I will consider your example. It would be nice if you would = revisit mine. > This would be valid under this RFC, right? No, because the method log() is implicitly void. WithLogger() expects a = Logger, not a null. But then I expect your example just had some logic = errors, no? BTW, in your example I am not sure I understand what `log()` does. I = assume it is logging a value? > But now as the author of class A I later want to change the default of = my withLogger method. Instead of just passing in new DatabaseLogger, I = now want to change my API default to just a LoggerType::DB enum for = reasons (What the reasons aren't relevant). >=20 > Today I don't have to think too hard about that change from an API BC = perspective because the consumer has either passed in a LoggerInterface = or a LoggerType -- or left it empty and used it's default value. I can = just change the default to LoggerType::DB and be on my way. The = downstream developer will never know or care that I did it because if = they wanted to call log() they had to first create their own instance = of LoggerInterface and have a reference to that object in their local = context like so: >=20 > $logger =3D LoggerType::DB->getLogger(); > (new A)->withLogger($logger); > $logger->log('B'); >=20 > With this RFC, now I can't change this API call without introducing a = BC break in my library because I have no idea at this point if some = downstream caller decided to use my default value directly or not. Okay, this I can work with. Thank you. =46rom the example you gave it appears that we can have a concrete = problem when:=20 1. There is a parameter with a default value,=20 2. That parameter is type-hinted,=20 3. The hinted type is declared as a union type, 4. An earlier version of the library initialized the default with a = value having one of the union types,=20 5. End-user developers used the library and then use `default` as an = expression of that type, and finally 6. The library developer changed the initialization of the `default` to = a different type from the union. Did I correctly identify the problematic use-case? Let us assume I did. Clearly this would be a BC break and the type you = are concerned with. Ok, so for argument sake, what if they revise the RFC to only allow = `default` to be used in an expression when the parameter is not = type-hinted with a union? Would that address your concern? Or are there = other use-cases that are problematic that do not hinge on the parameter = being type-hinted as a union type?=20 Note they could also propose a default value be able to be set for each = type in a union, with the first one being the default default if all = else is equal. That might not be exactly to your liking, but it seems = like it could address your stated problematic use-case. BTW, you could also guard against that problem you claim you cannot = guard against by ensuring your parameters with defaults are single types = with no extraneous methods. In your example if you instead used a = LoggerGetter interface with a single method GetLogger() you would = sidestep the problem you are concerned with completely, and you might = end up with a better API, too.: public function withLogger(LoggerGetterInterface $a =3D new = DatabaseLoggerGetter): static { $this->log =3D $a->getLogger(); return $this; =20 } > You can argue if this is a good API design or not, but it was only = written to provide a real example of how pulling the default value = higher up the call chain and allowing it to be used in expressions is = problematic for library authors all to save a couple of lines of code on = the consumer side. FWIW, I do not see the RFCs benefit as "saving lines of code" so much as = instead "improving clarity of code." > I'm honestly not sure what you're asking here. PHP currently doesn't = allow you access to the "default value" of a function you are calling = (maybe Reflection? I don't know offhand). You wrote (paraphrasing) "Developers shouldn't be allowed to access = defaults because they API did not explicitly allow them to." Well then, = it is simple completion of the binary logic to ask "How then do they = explicitly give permission?" IOW, if the argument is it can't be accessed because of lack of explicit = permission it seems maybe what is needed is an RFC to decide: 1. Should defaults just be assumed to be part of the public API, and=20 2. If no, then how can defaults be explicitly made public? =20 Maybe this? function withLogger(Logger $a =3D public new Logger): Logger {...} > So yes, I am pointing out a problem but not providing a solution = because I don't currently agree a solution is even needed. Fair enough. OTOH, depending on the number who support this (I have no idea the = number) you might be in a position that you'll get what you don't want = unless you can propose a solution that addresses your concerns while = also meeting their needs to. Just something to consider. > On Aug 26, 2024, at 3:28 AM, Rowan Tommins [IMSoP] = <imsop.php@rwec.co.uk> wrote: > I was responding to someone justifying anything and everything the = proposal allows, because Reflection already allows it. If the feature = was "first class syntax to access private methods of a class", I don't = think it would be controversial to challenge it. Saying "Reflection can = already do it" would be a poor defence, because part of Reflection's job = is to break the normal rules of the language. But saying that we can be certain default values are private and we want = to keep them that way is provably false by the existence of Reflection. =20= Yes, I get that it is more likely people would use this `default` = keyword in this way that they would use Reflection but not permitting = default as expression that does not provide any more _guarantee_ they = won't then they already have. > The overriding rule, in my head, is that the caller shouldn't need to = know, and shouldn't be able to find out, what a particular = implementation has chosen as the default. So the particularly = problematic operations are things like this:=20 >=20 > foo($whatWasTheDefault=3Ddefault)=20 > foo(logAndReturn(default)) > foo(doSomethingUnrelated(default) && false?: default)=20 >=20 > I can see people inventing use cases for these as soon as they're = available, but by doing so they will be completely changing the meaning = of default parameters, which currently mean "I trust the implementation = to substitute something valid here, and have no interest in what it is". Yes, that is the way it has (mostly) been, but justifying your argument = that (you believe) the caller shouldn't know the default's value simply = because it is currently not (generally) accessible is a poor defense for = why it should not be accessible. Or as they say; live by the sword, die = by the sword. It is unrealistic to say that a developer shouldn't know anything about = what a default value is because =E2=80=94 without knowing what what the = default actually is =E2=80=94 how does a developer know the default = value is the proper value for their use-case? Given that, I think there is an argument to be made that default values = *should* be made public and accessible and that developers who write = functions should plan accordingly. (I am not 100% sure I buy the = argument I just made yet, but I am also not sure that I do not.). Making = it part of the signature would make even more behavior explicit, and in = most cases that is a good thing. > Note that under inheritance, the default value may even change type: >=20 > class A { public function foo(int $bar=3D42) { ... } } > class B extends A { public function foo(int|string $bar=3D'hello') { = ... } } *Arguably*, that could violate LSP. But even if not, it could violate = the principle of least astonishment. OTOH, you are still presenting abstract hypotheticals with no evidence = that there exists use-case where developers would actually do what you = are worried about them doing. How about providing a concrete example = use-case as John Coggeshall did? Note we discovered a specific = problematic use-case that might be avoided or worked-around by our = collaborating in that way. > You ask how a library can provide access to that default, and the = answer is generally pretty trivial: define a public constant, and refer = to it in the parameter definition.=20 A global? Really? Yes, you can namespace them, but that doesn't tie them to the function = or the class. Too bad.=20 > The only exception I think is "new in initializer", where you would = have to provide a function/method instead of a constant, and couldn't = currently reuse it in the actual signature. Besides that Mrs Lincoln, how was the play? Oops, sorry. I forgot you were not an American. ;-) > Aside: one of those examples brings up an interesting question: is the = value pulled out by "default" calculated only once, or each time it's = mentioned? In other words, would this create 3 pointers to the same = object, or 3 different objects?=20 >=20 > foo(array|Something $x=3Dnew Something); > foo([default, default, default]); That is a question for the RFC author. > On Aug 26, 2024, at 5:03 AM, Andreas Leathley <a.leathley@gmx.net> = wrote: > When I have the GzipCompression class, I would know there is a default > value for $level, but when using the interface there might or might = not > be a default value, depending on the implementation. As far as I read > the RFC, using "default" when there is no default would lead to a > runtime exception, but there is no way of finding out if there is a > default if you do not already know. Being able to test that could be > useful, although I am not sure about the syntax for that.=20 Great catch! > On Aug 26, 2024, at 6:32 AM, Andreas Heigl <andreas@heigl.org> wrote: > I think I am missing something here. =46rom my understanding we are = *either* coding against the interface and then it should not be possible = to use `default` at all as no default is set in the interface. So the = fatal error is totally valid for me. Maybe default value for method parameters in interfaces is something the = RFC should consider enabling?=20 -Mike=