Re: Two new functions array_first() and array_last()
| From: | Larry Garfield | Date: | Wed, 18 Oct 2023 17:42:13 +0000 |
| Subject: | Re: Two new functions array_first() and array_last() | ||
| References: | 1 2 3 4 5 6 7 8 9 10 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-121415@lists.php.net to get a copy of this message | ||
On Wed, Oct 18, 2023, at 3:37 PM, Deleu wrote:
> I believe my reservations have been registered, I would not like to have
>
array_first() behave in a non-obvious way (i.e. involving array
> $key)
> because it's useful for Fibers users. I believe that writing your own
> wrapper that always returns key and value simultaneously is easy and
> straight-forward and may even make it into the core in the future. Rather
> than trying to solve deep convoluted issues on version 1, I believe PHP
> could introduce the simplest version of the function first and later on add
> an array_first_key_value() in the future if needed.
I think there's a fundamental misunderstanding here. Fibers are not threads. They do not do
preemptive multitasking. If a give function call stack does not explicitly suspend itself, then a
fiber WILL NOT change the current context.
If we assume that array_first(), being an internal function, does not explicitly suspend a fiber
internally (which I think is a pretty safe assumption), then this code cannot possibly have a race
condition in it:
$key = array_key_first($arr);
$value = array[$key];
The concerns about this having some kind of race condition around fibers are, as far as I am aware,
entirely unfounded, and we should not consider them, as they do not exist. (Obviously if those two
lines are separated by some other call, and that other call suspends a fiber, then it's
possible there would be a race condition. But in that case the operation is not atomic anyway so
all bets are completely off. Which is why you shouldn't be using shared mutable state arrays
at all in the first place, fibers are not, but that's another story.)
As far as the not-found error case goes, I cite my previous article on the subject here:
https://peakd.com/hive-168588/@crell/much-ado-about-null
More to the point, PHP arrays are untyped. That means there is absolutely no possible sentinel
value that we can guarantee will never be an array value, because even if we defined some one-off
enum to serve as a sentinel, that could be put into an array quite easily, even if it's dumb to
do. null is the default sentinel value, but runs into various problems because its
meaning in different contexts may be different.
The question is whether null being a legit value in an array being accessed with
array_first/array_last is a common *enough* case that we need to worry about it. I highly doubt it,
frankly. I cannot think of a non-contrived case where that would be true. (An array with random
null values in it that you don't want to filter out entirely seems like an edge case to me.)
That means for the vast majority of use cases, this is completely fine:
$v = array_first($arr) ?? $default;
Because the cases where you care to differentiate between "there is no first value" and
"there is a first value but it is a non-value" are small. And, as others have noted, in
those cases count() isn't hard to use.
So no, we don't need to do anything fancier than that.
To the other points raised:
Absolutely not to an in/out by-ref parameter. That is an abomination and I will vote against any
API that does that, without hesitation. Let's not even discuss it.
As far as the naming, most array functions that have a value version and key version don't
specify "value", only the key. The unspecified version is the value version. (array_diff
vs array_diff_key, array_intersect vs array_intersect_key, sort vs ksort, etc.) Pretty much nowhere
do we specify "_value" in the function name.
https://www.php.net/manual/en/ref.array.php
That means array_first/array_last is consistent with the current API, and array_first_value would be
the confusing oddball. For that reason, no, we should not do that and just use
array_first/array_last.
With all that said, I do support simple array_first/array_last functions, with just a
null-for-not-found return.
--Larry Garfield