diff --git a/CHANGELOG.md b/CHANGELOG.md index c6dda334..7fbd54ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - When a deferred `Set` or `Sequence` is used while iterating over itself it could produce unexpected results such as infinite loops or skipped values. - Fix iterating over a closed `\Generator` on a deferred `Set`/`Sequence` when the source monad no longer exist and the monad at hand has already been iterated over. +- A deferred `Sequence` would load an extra element that is never used when calling `take`. ## 5.11.2 - 2025-02-23 diff --git a/proofs/sequence.php b/proofs/sequence.php index 9dcaada1..22269936 100644 --- a/proofs/sequence.php +++ b/proofs/sequence.php @@ -435,6 +435,27 @@ static function($assert, $values) { }, ); + yield proof( + 'Sequence::defer()->take() should not load an extra element', + given( + Set\Sequence::of(Set\Type::any()), + ), + static function($assert, $values) { + $sequence = Sequence::defer((static function() use ($values) { + yield from $values; + + throw new Exception; + })())->take(\count($values)); + + $assert->not()->throws( + static fn() => $assert->same( + $values, + $sequence->toList(), + ), + ); + }, + ); + yield test( 'Partial load a deferred Sequence appended to a lazy one', static function($assert) { diff --git a/src/Sequence/Defer.php b/src/Sequence/Defer.php index a0dcc73a..bc812b4b 100644 --- a/src/Sequence/Defer.php +++ b/src/Sequence/Defer.php @@ -625,6 +625,10 @@ public function take(int $size): Implementation /** @psalm-suppress ImpureFunctionCall */ return new self( (static function(int $size) use (&$captured): \Generator { + if ($size === 0) { + return; + } + $taken = 0; /** * @psalm-suppress PossiblyNullArgument @@ -634,14 +638,15 @@ public function take(int $size): Implementation $values->rewind(); while ($values->valid()) { + yield $values->current(); + ++$taken; + if ($taken >= $size) { $values->cleanup(); return; } - yield $values->current(); - ++$taken; $values->next(); } })($size),