-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Proposal: Add iterable\any(iterable $input, ?callable $cb=null), all(...), none(...), find(...), reduce(...) #6053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 1 commit
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
5aadb06
Add PHP\iterable\any(...) and all(iterable $input, ?callable $cb=null)
bugreportuser b300cbe
Use Z_PARAM_ITERABLE macro, change namespace
TysonAndre 7f3cf58
Add iterable\none(iterable $iterable, ?callable $callback=null):bool
TysonAndre 06310e1
Implement iterator\reduce($carry, $item): mixed
TysonAndre 6a01897
Implement iterable\find($iterable, $callback, $default=null): mixed
TysonAndre 209e429
Remove unnecssary inlining
morrisonlevi 8217ce5
Remove $initial from reduce; throw on empty
morrisonlevi 4d02c6f
Fix enum type
morrisonlevi c826269
Fix test failure
TysonAndre File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Implement iterator\reduce($carry, $item): mixed
Semantics are similar to array_reduce
- Loading branch information
commit 06310e1ee5866bee5f802ad524bf099900bd5cd7
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
--TEST-- | ||
Test reduce() function | ||
--FILE-- | ||
<?php | ||
|
||
use function iterable\reduce; | ||
|
||
/* | ||
Prototype: mixed iterable\reduce(array $array, callable($carry, $item): mixed $callback); | ||
Description: Iterate over iterable and reduce | ||
*/ | ||
|
||
function dump_reduce(...$args) { | ||
try { | ||
var_dump(reduce(...$args)); | ||
} catch (Error $e) { | ||
printf("Caught %s: %s\n", $e::class, $e->getMessage()); | ||
} | ||
} | ||
|
||
// The result of strtolower is locale-dependent, meaning that it cannot be converted to a constant by opcache. | ||
dump_reduce([]); | ||
dump_reduce([], function () {}, strtolower('TEST')); | ||
dump_reduce(['x', 'y', 'z'], function ($carry, $item) { $carry .= $item; return $carry; }, strtolower('TEST')); | ||
dump_reduce([strtolower('WORLD'), '!'], function ($carry, $item) { $carry .= $item; return $carry; }, strtolower('HELLO')); | ||
dump_reduce([strtolower('WORLD')], function (string $carry, string $item): string { return $carry . $item; }, strtolower('HELLO')); | ||
|
||
?> | ||
--EXPECT-- | ||
Caught ArgumentCountError: iterable\reduce() expects at least 2 arguments, 1 given | ||
string(4) "test" | ||
string(7) "testxyz" | ||
string(11) "helloworld!" | ||
string(10) "helloworld" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
--TEST-- | ||
Test reduce() function on Traversable | ||
--FILE-- | ||
<?php | ||
|
||
use function iterable\reduce; | ||
|
||
/* | ||
Prototype: mixed iterable\reduce(array $array, callable($carry, $item): mixed $callback); | ||
Description: Iterate over iterable and reduce | ||
*/ | ||
|
||
function dump_reduce(...$args) { | ||
try { | ||
var_dump(reduce(...$args)); | ||
} catch (Error $e) { | ||
printf("Caught %s: %s\n", $e::class, $e->getMessage()); | ||
} | ||
} | ||
|
||
function generate_strings() { | ||
yield strtoupper('Hello'); | ||
yield ' '; | ||
yield strtoupper('World!'); | ||
return strtoupper('UNUSED'); | ||
} | ||
|
||
// The result of strtolower is locale-dependent, meaning that it cannot be converted to a constant by opcache. Also, test reference counting. | ||
dump_reduce(new ArrayObject([])); | ||
dump_reduce(new ArrayObject([]), function () {}, strtolower('TEST')); | ||
dump_reduce(new ArrayObject(['x', 'y', 'z']), function ($carry, $item) { $carry .= $item; return $carry; }, strtolower('TEST')); | ||
dump_reduce(new ArrayObject([strtolower('WORLD'), '!']), function ($carry, $item) { $carry .= $item; return $carry; }, strtolower('HELLO')); | ||
dump_reduce(new ArrayObject([strtolower('WORLD')]), function (string $carry, string $item): string { return $carry . $item; }, strtolower('HELLO')); | ||
dump_reduce(generate_strings(), function (string $carry, string $item): string { return $carry . $item; }, ''); | ||
dump_reduce(generate_strings(), function ($carry, $item): string { $item = $carry . $item; unset($carry);return $item; }, ''); | ||
// Passing by reference is not supported. | ||
dump_reduce(generate_strings(), function (string &$carry, string $item): string { $carry .= $item; return $carry;}, ''); | ||
|
||
?> | ||
--EXPECTF-- | ||
Caught ArgumentCountError: iterable\reduce() expects at least 2 arguments, 1 given | ||
string(4) "test" | ||
string(7) "testxyz" | ||
string(11) "helloworld!" | ||
string(10) "helloworld" | ||
string(12) "HELLO WORLD!" | ||
string(12) "HELLO WORLD!" | ||
|
||
Warning: {closure}(): Argument #1 ($carry) must be passed by reference, value given in %s on line 12 | ||
|
||
Warning: {closure}(): Argument #1 ($carry) must be passed by reference, value given in %s on line 12 | ||
|
||
Warning: {closure}(): Argument #1 ($carry) must be passed by reference, value given in %s on line 12 | ||
string(12) "HELLO WORLD!" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly prefer removing
$initial
and addingfold
which always requires it:The
fold
function doesn't throw on empty, and thereduce
will. This pattern exists in other languages, such as Kotlin.I'm happy to do this work if you'll agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the inconsistency with array_reduce (which has optional $initial=null) would have more objectors for making it harder to learn the language or switch code from array_reduce to
*reduce
intuitively for beginners.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an error condition in
reduce
where there is not an initial value and an empty iterable, and it should throw because there is no legal value we can return that isn't already possible in the reduction. We should not repeat the mistakes of the past. You argue in another comment thatfind
is useful in other languages, and yet you don't buy that same argument here? What gives?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this again, I'd agree my earlier proposal for reduce was a mistake and it's worth changing, adding fold and either removing reduce entirely or requiring a non-empty array.
The other argument was about including a function, not a change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can work on
fold
tonight.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you okay with these signatures and semantics for
fold
andreduce
?I imagine there is some discussion to be had for naming the parameters to make sure named parameters is a good experience, so let me know what you think. The name
$into
I picked from Swift. I used$by
because it's short but not an abbreviation likeacc
; my quick glance in other languages' docs didn't turn up anything better.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For fold, I think having the callback third would be hard to remember when it's second in other reduce() function
The inner implementation seem reasonable enough. I assume the ArrayObject is just for illustrating the behavior and not the internal implementation
$seq seems like an harder to remember naming choice compared to $array/$iterable used elsewhere - https://www.php.net/manual/en/function.iterator-apply.php and https://www.php.net/manual/en/function.array-walk.php - especially for non-english speakers
PHP's already using $initial for https://www.php.net/manual/en/function.array-reduce.php and I don't see a strong reason to switch to a different name - initial's been used elsewhere (e.g. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
ArrayIterator
is just for showing behavior, yes. Notably, this will not passNULL
as the first parameter to the callback on the very first time it is called, unlikearray_reduce
and what this PR currently does.As an example with the data set
[1, 3, 5, 7]
:This will print:
And not:
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented the changes to
reduce
on this branch: https://github.com/morrisonlevi/php-src/tree/levi/any-all-iterable-checks. For some reason it wouldn't let me select your fork as the merge-base, so I didn't open a PR, but you can look at the last two commits. I did not yet addfold
.