Варварский генератор



Недавно с коллегами обсудили, что, строго говоря, генератор не соблюдает принцип подстановки Барбары Лисков. Он кидает исключение при попытке перемотки после начала обхода и тем самым нарушает контракт Iterable, где такое поведение не предусмотрено.



Понятно, почему конструктивно так сделали. Тем не менее передача генератора в функцию, которая принимает Traversable или iterable, приводит к WTF казусу, если она вполне законно пытается обойти аргумент больше одного раза.



Если очень хочется, чтобы инстанс генератора отрабатывал многократно, и при этом вы уверены, что это не приведёт к ненужным побочным эффектам, можно обернуть функцию, порождающую генератор, в RewindableGenerator:



/**

* @template TKey

* @template TValue

* @implements IteratorAggregate<TKey, TValue>

*/

final class RewindableGenerator implements IteratorAggregate

{

/**

* @param callable(): Generator<TKey, TValue> $generatorFunction

*/

public function __construct(private $generatorFunction) {}



public function getIterator(): Traversable

{

return ($this->generatorFunction)();

}

}



Вот как это будет работать: https://3v4l.org/46Evq.



Пишите в комментариях, где ещё PHP нарушает LSP.