On Aug 25, 2024, at 14:46, Rowan Tommins [IMSoP] <imsop....@rwec.co.uk> wrote:
> 
> On 25/08/2024 18:44, John Bafford wrote:
> 
>> Although I'm not sold on the idea of using default as part of an 
>> expression, I would argue that a default function parameter value is 
>> fair game to be read and manipulated by callers. If the default value 
>> was intended to be private, it shouldn't be in the function declaration.
> 
> There's an easy argument against this interpretation: child classes can 
> freely change the default value for a parameter, as long as they do not make 
> it mandatory. https://3v4l.org/SEsRm
> That matches my intuition: that the public API, as a contract, states that 
> the parameter is optional; the specification of what happens when it is not 
> provided is an implementation detail.
> For comparison, consider constructor property promotion; the caller shouldn't 
> know or care whether a class is defined as:
> public function __construct(private int $bar) {}
> or:
> private int $my_bar;
> public function __construct(int $bar) { $this->my_bar = $bar; }
> The syntax sits in the function signature because it's convenient, not 
> because it's part of the API.

This is only by current convention. It used to be that parameter names were not 
part of the API contract, but now with named parameters, they are. There's no 
reason default values couldn't (or shouldn't) become part of the API contract 
in the same way.

(Note that in some other languages, default parameter values are not only part 
of the API contract, but they're emitted into the clients when compiled, so an 
API can change/add/remove its default values and the client continues to 
function as it used to with the value as defined at compile time. This doesn't 
currently matter for PHP, where you have the full source to anything you run, 
but could become important later if PHP gained ahead-of-time compiled binary 
modules.)


>> One important case where reading the default value could be important is
>> in interoperability with different library versions. For example, a 
>> library might change a default parameter value between versions. If 
>> you're using the library, and want to support both versions, you might 
>> both not want to set the value, and yet also care what the default value
>> is from the standpoint of knowing what to expect out of the function.
> 
> This seems contradictory to me. If you use the default, you're telling the 
> library that you don't care about that parameter, and trust it to provide a 
> default.
> If you want to know what the library did with its arguments, reflecting the 
> signature will never be enough anyway. For example, it's quite common to 
> write code like this:
> function foo(?SomethingInterface $blah = null) {
>     if ( $blah === null ) {
>         $blah = self::_setup_default_blah();
>     }
>     // ...
> }
> A caller can't tell by looking at the signature that a new version of the 
> library has changed what _setup_default_blah() returns. If the library 
> doesn't provide an API to get $blah out later, then it's a private detail 
> that the caller has no business inspecting.

Well, but that's the private default example I described. In that case you're 
not intended to be able to reason about what the default is, because it's a 
private implementation detail of the function, as opposed to being expressed in 
the parameter list. Although, if it weren't intended to be an implementation 
detail, the only thing stopping you from writing in the parameter list like 
this:

        function foo(SomethingInterface $blah = self::_setup_default_blah()) 
{...}

is because PHP doesn't currently allow default values to be computed at 
runtime. (Maybe it should.)

-John

Reply via email to