Skip to content

Commit 23d3bb0

Browse files
committed
Merge branch 'develop'
* develop: (32 commits) specify next release add missing test fix loading an extra element when not necessary add test for in memory nested loops to make sure cursors are correct mention the bug fix on closed generators leave the primitive iterator in the same state as it was completely iterated over mention why there is no cleanup at the end of a lazy iterator make sure to load the value to return before cleaning up, and possibly changing the underlying value being fetched inline the call to yield the generator value discard psalm errors update changelog fix always keeping intermediary values when not necessary add comment to explain the weak reference trick add missing tests fix capturing accumulate iterators when the source wrapper no longer exist but the source generator is share elsewhere test cleanup on partial zip remove accumulate tests has it is too low level to test add tests to make sure partial loading does not have side effects CS re-position the cursor of a deferred sequence when partially iterated over ...
2 parents babe64c + 3693bf5 commit 23d3bb0

21 files changed

Lines changed: 1561 additions & 424 deletions

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## 5.11.3 - 2025-02-26
4+
5+
### Changed
6+
7+
- The function passed to `RegisterCleanup` for lazy `Set`s or `Sequence`s is not called when the monad is partially loaded when called from a deferred/in memory `Set`/`Sequence` (such as `Sequence::zip()`, a call to `find` inside a `flatMap`, etc...).
8+
9+
### Fixed
10+
11+
- When a deferred `Set` or `Sequence` is used while iterating over itself it could produce unexpected results such as infinite loops or skipped values.
12+
- 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.
13+
- A deferred `Sequence` would load an extra element that is never used when calling `take`.
14+
315
## 5.11.2 - 2025-02-23
416

517
### Fixed

proofs/sequence.php

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,4 +434,207 @@ static function($assert, $values) {
434434
);
435435
},
436436
);
437+
438+
yield proof(
439+
'Sequence::defer()->take() should not load an extra element',
440+
given(
441+
Set\Sequence::of(Set\Type::any()),
442+
),
443+
static function($assert, $values) {
444+
$sequence = Sequence::defer((static function() use ($values) {
445+
yield from $values;
446+
447+
throw new Exception;
448+
})())->take(\count($values));
449+
450+
$assert->not()->throws(
451+
static fn() => $assert->same(
452+
$values,
453+
$sequence->toList(),
454+
),
455+
);
456+
},
457+
);
458+
459+
yield test(
460+
'Partial load a deferred Sequence appended to a lazy one',
461+
static function($assert) {
462+
$lazy = Sequence::lazy(static function() {
463+
yield 1;
464+
yield 2;
465+
yield 3;
466+
yield 4;
467+
yield 5;
468+
});
469+
$defer = Sequence::defer((static function() {
470+
yield 6;
471+
yield 7;
472+
yield 8;
473+
yield 9;
474+
yield 10;
475+
})());
476+
477+
$assert->same(
478+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 6, 7, 8, 9, 10],
479+
$lazy
480+
->append($defer)
481+
->take(7)
482+
->flatMap(static fn($i) => match (true) {
483+
$i > 5 => $defer,
484+
default => Sequence::of($i),
485+
})
486+
->toList(),
487+
);
488+
$assert->same(
489+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 6, 7, 8, 9, 10],
490+
$defer
491+
->prepend($lazy)
492+
->take(7)
493+
->flatMap(static fn($i) => match (true) {
494+
$i > 5 => $defer,
495+
default => Sequence::of($i),
496+
})
497+
->toList(),
498+
);
499+
},
500+
);
501+
502+
yield test(
503+
'Sequence::defer()->zip() with a partially loaded lazy Sequence',
504+
static function($assert) {
505+
$defer = Sequence::defer((static function() {
506+
yield 1;
507+
yield 2;
508+
yield 3;
509+
})());
510+
$cleaned = false;
511+
$loaded = false;
512+
$lazy = Sequence::lazy(static function($register) use (&$cleaned, &$loaded) {
513+
$register(static function() use (&$cleaned) {
514+
$cleaned = true;
515+
});
516+
517+
yield 4;
518+
yield 5;
519+
yield 6;
520+
yield 7;
521+
$loaded = true;
522+
});
523+
524+
$assert->same(
525+
[[1, 4], [2, 5], [3, 6]],
526+
$defer
527+
->zip($lazy)
528+
->toList(),
529+
);
530+
$assert->true($cleaned);
531+
$assert->false($loaded);
532+
$assert->same([1, 2, 3], $defer->toList());
533+
$assert->same([4, 5, 6, 7], $lazy->toList());
534+
},
535+
);
536+
537+
yield test(
538+
'Lazy Sequence::toSet()',
539+
static function($assert) {
540+
$loaded = false;
541+
$lazy = Sequence::lazy(static function() use (&$loaded) {
542+
yield 1;
543+
yield 2;
544+
yield 3;
545+
$loaded = true;
546+
});
547+
548+
$set = $lazy->toSet();
549+
$assert->false($loaded);
550+
$assert->same([1, 2, 3], $set->toList());
551+
$assert->true($loaded);
552+
},
553+
);
554+
555+
yield test(
556+
'Deferred Sequence::filter() is iterable twice',
557+
static function($assert) {
558+
$defer = Sequence::defer((static function() {
559+
yield 1;
560+
yield 2;
561+
yield 3;
562+
yield 4;
563+
})());
564+
$odd = $defer->filter(static fn($i) => $i%2 === 0);
565+
566+
$assert->same([2, 4], $odd->toList());
567+
$assert->same([2, 4], $odd->toList());
568+
$assert->same([1, 2, 3, 4], $defer->toList());
569+
},
570+
);
571+
572+
yield test(
573+
'Consuming out of order deferred sequences',
574+
static function($assert) {
575+
$source = Sequence::defer((static function() {
576+
yield from \range(0, 10);
577+
})());
578+
$initial = $source->filter(static fn($i) => $i%2 === 0);
579+
$other = $source->filter(static fn() => false);
580+
unset($source);
581+
582+
$assert->false($initial->equals($other));
583+
},
584+
);
585+
586+
yield test(
587+
'Calling first inside a lazy Sequence::flatMap()',
588+
static function($assert) {
589+
$lazy = Sequence::lazy(static function() {
590+
yield 1;
591+
yield 2;
592+
yield 3;
593+
});
594+
595+
$assert->same(
596+
[1, 1, 1],
597+
$lazy
598+
->flatMap(static fn() => $lazy->first()->toSequence())
599+
->toList(),
600+
);
601+
},
602+
);
603+
604+
yield proof(
605+
'Sequence::aggregate() should not alter the initial Sequence by default',
606+
given(Set\Sequence::of(Set\type::any())),
607+
static function($assert, $values) {
608+
$inMemory = Sequence::of(...$values);
609+
610+
$assert->same(
611+
$values,
612+
$inMemory
613+
->aggregate(static fn($a, $b) => Sequence::of($a, $b))
614+
->toList(),
615+
);
616+
617+
$defer = Sequence::defer((static function() use ($values) {
618+
yield from $values;
619+
})());
620+
621+
$assert->same(
622+
$values,
623+
$defer
624+
->aggregate(static fn($a, $b) => Sequence::of($a, $b))
625+
->toList(),
626+
);
627+
628+
$lazy = Sequence::lazy(static function() use ($values) {
629+
yield from $values;
630+
});
631+
632+
$assert->same(
633+
$values,
634+
$lazy
635+
->aggregate(static fn($a, $b) => Sequence::of($a, $b))
636+
->toList(),
637+
);
638+
},
639+
);
437640
};

proofs/set.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,130 @@ static function($assert, $values) {
5252
);
5353
},
5454
);
55+
56+
yield test(
57+
'Set defer nesting calls',
58+
static function($assert) {
59+
$set = Set::defer((static function() {
60+
yield 1;
61+
yield 2;
62+
yield 3;
63+
})());
64+
65+
$assert->same(
66+
[
67+
[1, 1],
68+
[1, 2],
69+
[1, 3],
70+
[2, 1],
71+
[2, 2],
72+
[2, 3],
73+
[3, 1],
74+
[3, 2],
75+
[3, 3],
76+
],
77+
$set
78+
->flatMap(static fn($i) => $set->map(
79+
static fn($j) => [$i, $j],
80+
))
81+
->toList(),
82+
);
83+
84+
$doubles = $set->map(static fn($i) => $i*2);
85+
86+
$assert->same(
87+
[
88+
[1, 2],
89+
[1, 4],
90+
[1, 6],
91+
[2, 2],
92+
[2, 4],
93+
[2, 6],
94+
[3, 2],
95+
[3, 4],
96+
[3, 6],
97+
],
98+
$set
99+
->flatMap(static fn($i) => $doubles->map(
100+
static fn($j) => [$i, $j],
101+
))
102+
->toList(),
103+
);
104+
},
105+
);
106+
107+
yield test(
108+
'Set nesting calls',
109+
static function($assert) {
110+
$set = Set::of(1, 2, 3);
111+
112+
$assert->same(
113+
[
114+
[1, 1],
115+
[1, 2],
116+
[1, 3],
117+
[2, 1],
118+
[2, 2],
119+
[2, 3],
120+
[3, 1],
121+
[3, 2],
122+
[3, 3],
123+
],
124+
$set
125+
->flatMap(static fn($i) => $set->map(
126+
static fn($j) => [$i, $j],
127+
))
128+
->toList(),
129+
);
130+
131+
$doubles = $set->map(static fn($i) => $i*2);
132+
133+
$assert->same(
134+
[
135+
[1, 2],
136+
[1, 4],
137+
[1, 6],
138+
[2, 2],
139+
[2, 4],
140+
[2, 6],
141+
[3, 2],
142+
[3, 4],
143+
[3, 6],
144+
],
145+
$set
146+
->flatMap(static fn($i) => $doubles->map(
147+
static fn($j) => [$i, $j],
148+
))
149+
->toList(),
150+
);
151+
},
152+
);
153+
154+
yield test(
155+
'Set defer partial nesting calls',
156+
static function($assert) {
157+
$set = Set::defer((static function() {
158+
yield 1;
159+
yield 2;
160+
yield 3;
161+
})());
162+
163+
$assert->same(
164+
[
165+
[1, 1],
166+
[2, 1],
167+
[3, 1],
168+
],
169+
$set
170+
->flatMap(
171+
static fn($i) => $set
172+
->find(static fn() => true)
173+
->toSequence()
174+
->toSet()
175+
->map(static fn($j) => [$i, $j]),
176+
)
177+
->toList(),
178+
);
179+
},
180+
);
55181
};

0 commit comments

Comments
 (0)