Pipe an infinite Generator through filter / map / take without ever materialising it. Learn which Arrays functions are safe (lazy and short-circuit) and which will hang forever (terminal).
// Naturals: 1, 2, 3, 4, ... forever.
$naturals = function () {
$i = 1;
while (true) yield $i++;
};$naturals() is a Generator that never ends. Passing it to anything that wants the “whole list” (array_filter, array_map, sort, count, …) will hang.
This library’s Arrays functions fall into three groups. Check the coloured badge at the top of each function’s doc page:
Use Tags → [lazy](/tags/lazy.html) / [short-circuit](/tags/short-circuit.html) / [terminal](/tags/terminal.html) to browse each group.
use PinkCrab\FunctionConstructors\Arrays;
use PinkCrab\FunctionConstructors\Numbers;
use PinkCrab\FunctionConstructors\Comparisons;
foreach (Arrays\take(5)($naturals()) as $n) {
echo $n . ' ';
}
// 1 2 3 4 5take(5) pulls five values, stops asking the source for more. The other 2,147,483,642 integers are never generated.
$first5Evens = F\compose(
Arrays\filter(Numbers\isMultipleOf(2)), // Numbers\isMultipleOf gives us a ready-made "is even?" predicate
Arrays\take(5)
);
foreach ($first5Evens($naturals()) as $n) {
echo $n . ' ';
}
// 2 4 6 8 10The pipeline pulls 1, tests (fail), pulls 2, tests (pass), yields, continues until 5 have yielded. At that point take(5) stops and the source stops advancing. No inline fn() — the predicate is constructed by another library function.
foreach (Arrays\takeUntil(Comparisons\isGreaterThan(100))($naturals()) as $n) {
echo $n . ' ';
}
// 1 2 3 ... 99 100$firstPrime = Arrays\filterFirst(function (int $n): bool {
if ($n < 2) return false;
for ($i = 2; $i * $i <= $n; $i++) if ($n % $i === 0) return false;
return true;
});
echo $firstPrime($naturals()); // 2Pulls 1 (not prime), pulls 2 (prime — match) → returns. Source advanced exactly twice.
Don’t mix infinite sources with terminal operations:
// ❌ hangs — sort needs the whole list
Arrays\sort()($naturals());
// ❌ hangs — fold needs to consume everything
Arrays\fold(fn($a, $b) => $a + $b, 0)($naturals());
// ❌ hangs — takeLast needs to know where the end is
Arrays\takeLast(5)($naturals());
// ❌ hangs — count materialises first
iterator_to_array($naturals());For these, force a bounded prefix first:
// ✅ sort the first 100 naturals
Arrays\sort()(iterator_to_array(Arrays\take(100)($naturals())));
// ✅ sum the first 100 naturals
Arrays\fold(fn($a, $b) => $a + $b, 0)(Arrays\take(100)($naturals()));$poll = function () {
while (true) {
yield fetchLatest();
sleep(5);
}
};
$firstFailed = Arrays\filterFirst(fn($r) => $r['status'] !== 200);
$failedResponse = $firstFailed($poll());
// Loops fetching every 5 seconds until one fails, returns that response.The pipeline is a live stream; filterFirst is the exit condition; no memory growth; no outer while (true) boilerplate.