(TL;DR; Down the thread a bit I put together a concrete example of why I'm 
opposed to this RFC)

On Aug 26 2024, at 12:44 am, Mike Schinkel <m...@newclarity.net> wrote:
> Speaking only to #1 for the moment, there are many different places in PHP 
> where certain expressions make no sense either, yet I do not see those 
> objecting calling the question for those other scenarios, and I ask myself 
> "Why not?"
>
> One obvious answer is "because those are status quo and this is not" but I 
> also maybe because the many non-sensical things that can be done elsewhere 
> are, in practice, not a problem as nobody ever does them. And why not? 
> Because...they are nonsensical. Given this the arguments against feel to me 
> to be rather bikesheddy. But I will come back to #1.

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.
> Moving on to #2 I respect the argument in theory. But as we are constantly 
> told the RFC author's burden is to prove the necessity, it seems only 
> reasonable and fair to expect that — when many people see the utility of and 
> support an RFC — that those who object to it should provide some concrete 
> examples of how their concerns would manifest problems rather than just say 
> "it might allow something bad."
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 have looked for but yet not seen any examples of how effectively publishing 
> a default value could actually cause a significant problem — and this is the 
> important part — in a real-world scenario and not just a contrived one.
> Sure if I have `foo(int $index=1)`, a developer calls with `foo(default*3)`, 
> and then the author of foo changes the signature to `foo(int $index=0)` 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?

How is that not a real-world contrived scenario? That is the problem. It's that 
some library developer decided to change the default value for their own 
purposes and now any code that was relying on that default value has broken. I 
don't think anyone needs to go hunting around GitHub or something to prove that 
people change default values between library releases, nor that this RFC would 
introduce a new dependency between upstream APIs and the code that uses them to 
manage. See below for a fairly reasonable example I whipped up.
> 3. "How likely are people to accidentally do that stupid thing?"
Experience has shown us time and time again that people will use the features 
that get implemented, and once they exist we are stuck with the consequences. 
Developers who consume APIs are rarely worrying about the developers who wrote 
those APIs. This is making it not only very easy to do a stupid thing, it's 
making the fact people are doing a stupid thing the problem of an entirely 
different project/developer who now has to deal with the reality any time they 
touch a default value they have to worry about the downstream impacts, being 
granted zero control to prevent it from happening. It's also something that 
would be hard for downstream developers to even realize was a problem...
Library dev: "BC break, I changed this default value"
Downstream dev: "Okay.... so how do I fix this code someone else wrote 2 years 
ago so I can upgrade?"
Library dev: "I dunno, depends on how you used default so you're on your own. I 
guess you better grep and hope you catch them all"

> But if you disallow `default!=5`, then you (likely?) also disallow 
> `default!=5 ? default : 0` and any other *sensical* expression with that 
> sub-expression. Do we really want to flesh out all the potential nonsensical 
> expressions for a given use-case and build a custom parser for this one RFC?
As I previously said, I don't think this would be addressed in the parser -- I 
think it'd have to be a runtime check (if such a check were to exist).
That aside, no I don't really want to flesh out all the potential expressions 
to build those rules into the engine. I believe if we are going to allow 
expressions for default values as proposed we _need_ to build those rules, but 
given the circumstances I think it's more likely the RFC needs to be 
reconsidered, updated to reflect feedback, or withdrawn.
> 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= new MyDependency) {...}
> doSomething((default)->WithLogger(new Logger));

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:
<?php
enum LoggerType: string
{
case DB = 'db';
case FILE = 'file';
case CLOUD = 'cloud';

public function getLogger(): LoggerInterface
{
return match($this)
{
static::DB => new DatabaseLogger(),
static::FILE => new FileLogger(),
static::CLOUD => new CloudLogger()
};
}
}

interface LoggerInterface
{
public function log(string $x);
}

// Assume DatabaseLogger, etc. exist and implement LoggerInterface
class A
{
protected ?LoggerInterface $log;

public function withLogger(LoggerInterface|LoggerType $a = new DatabaseLogger): 
static
{
if($a instanceof LoggerInterface)
{
$this->log = $a;
return $this;
}

$this->log = $a->getLogger();
return $this;
}
}

(new A)->withLogger((default)->log('B'));
This would be valid under this RFC, right? 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).
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:
$logger = LoggerType::DB->getLogger();
(new A)->withLogger($logger);
$logger->log('B');

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.
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.
> > On Aug 25, 2024, at 12:23 PM, John Coggeshall <j...@coggeshall.org> wrote:
> > I won't vote for this RFC if the above code is valid, FWIW. Unlike include 
> > , default is a special-case with a very specific purpose -- one that is 
> > reaching into someone else's API in a way the developer of that library 
> > doesn't explicitly permit.

> Ok, so if a developer of the API currently wants to indicate that other 
> developers CAN explicitly reach in then how would they do that, currently?
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).
> Your argument seems to be it should implicitly be disallowed, but I do not 
> see any way in which you are proposing a developer could explicitly empower a 
> developer to allow it. At least not without a ton of boilerplate which, for 
> each default value, turns into a lot of extra code to create and then 
> maintain.
> It seems one-sided to argue against allowing something that "has not been 
> explicitly allowed" if there is no way to explicitly allow it. And I get you 
> may say "well that's not my job" to which —if you did — I would ask "Do you 
> really only want to be the one who brings the problem but not the solution?"

Right now there isn't a problem. There is nothing lacking in the current 
language that prevents me from providing my own LoggerInterface or LoggerType 
to the withLogger above. This is a suggestion to expand the syntax of the 
language to simplify something, not enable it to exist where before it didn't. 
The RFC would introduce something that makes it marginally easier for a 
downstream consumer of the API, at the cost of making life painful for library 
authors and worse not providing any way to prevent it from happening. So yes, I 
am pointing out a problem but not providing a solution because I don't 
currently agree a solution is even needed.
Not all ideas make the cut the first time, and that's okay. Those of us who 
have stood up against this idea in its current form have tried to offer our 
best thoughts as to how you might make it work by outlining that we think there 
need to be rules around the types of expressions that would be allowed... these 
opinions don't come out of nowhere or meant to be obstructionist -- still, they 
have largely been dismissed with strawman counters about how include statements 
let you do silly things, or responses like "The truth always lies in the code, 
which is why RFC authors are strongly encouraged to pursue patches where there 
is doubt, and similarly, I think counter-proposals on the mailing list should 
follow suit, otherwise we can find ourselves arguing over nothing." -- I don't 
agree with that, or feel obligated to attempt to write patches for an idea I 
don't think is necessary in the first place.
John

Reply via email to