From 4fe9da0167be3e895916c4926988cf36bd7a153e Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Tue, 27 Oct 2026 23:04:39 +0100 Subject: [PATCH] pass only carry and value to two-parameter reducers --- src/Collections/queries.php | 7 ++++--- tests/Collections/QueriesTest.php | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Collections/queries.php b/src/Collections/queries.php index 0875ecd..b4d0229 100644 --- a/src/Collections/queries.php +++ b/src/Collections/queries.php @@ -25,7 +25,6 @@ use function Kadet\Functional\Internal\callback; use function Kadet\Functional\Collections\Internal\extremum; use function Kadet\Functional\Collections\Internal\firstOf; use function Kadet\Functional\Collections\Internal\reversedEntries; -use function Kadet\Functional\compile; /** * Returns the first value for which `$predicate($value, $key)` is truthy, or the first value without a predicate. @@ -288,6 +287,8 @@ function max(?callable $selector = null): \Closure /** * Folds the collection into a single value with `$fn($carry, $value, $key)`, starting from `$initial`. * + * Internal functions (e.g. `max(...)`) and functions declaring two parameters get only `($carry, $value)`. + * * @template TKey = mixed * @template TValue = mixed * @template TCarry = mixed @@ -299,8 +300,8 @@ function max(?callable $selector = null): \Closure */ function reduce(callable $fn, mixed $initial = null): \Closure { - // The reducer takes the carry first, so it is compiled without the ($value, $key) adaptation. - $fn = compile($fn); + // The reducer takes the carry first: an internal one, or one declaring two parameters, gets ($carry, $value). + $fn = callback($fn, 2); return static function (iterable $data) use ($fn, $initial) { $carry = $initial; diff --git a/tests/Collections/QueriesTest.php b/tests/Collections/QueriesTest.php index 88b24eb..fb4cc42 100644 --- a/tests/Collections/QueriesTest.php +++ b/tests/Collections/QueriesTest.php @@ -9,6 +9,7 @@ use Ds\Map; use Ds\Seq; +use Kadet\Functional as f; use Kadet\Functional\Collections as c; use Kadet\Functional\Exception\EmptyCollectionException; @@ -204,6 +205,24 @@ class QueriesTest extends \PHPUnit\Framework\TestCase $this->assertNull(c\reduce(fn ($carry, $v) => $v)([])); } + public function testReduceWithInternalFunctionGetsCarryAndValueOnly() + { + $this->assertSame(2, c\reduce(max(...), 0)([5 => 1, 9 => 2])); + } + + public function testReduceWithDecoratedTwoParameterClosureGetsCarryAndValueOnly() + { + $calls = []; + $reducer = f\memoize(function ($carry, $v) use (&$calls) { + $calls[] = func_get_args(); + + return $carry + $v; + }); + + $this->assertSame(3, c\reduce($reducer, 0)(['a' => 1, 'b' => 2])); + $this->assertSame([[0, 1], [1, 2]], $calls); + } + public function testSum() { $this->assertSame(6, c\sum()([1, 2, 3]));