Hi Levi Morrison, > > > Currently, there don't seem to be any internal classes that can be used > > > to store a copy of the keys and values of an arbitrary Traversable. > > > > > > - This would help in eagerly evaluating the result of a generator in a > > > memory efficient way that could be exactly stored and reused > > > e.g. `return $this->cachedResults ??= new > > >\RewindableKeyValueIterator($this->innerResultsGenerator());` > > > https://externals.io/message/108767#108797 > > > - This would be useful to exactly represent the keys of sequences with > > > repeated keys (e.g. `yield 'first'; yield 'second';` implicitly uses the > > > key `0` twice.) > > > - This would be convenient to have to differentiate between 1, '1', and > > > true. > > > - This would be useful if php were to add internal global functions that > > > act on iterables and return Traversables with potentially repeated keys > > > based on those iterables, > > > e.g. map(), filter(), take(), flip(), etc > > > - If PHP were to add more iterable methods, being able to save an > > > immutable copy of a traversable's result would be useful for end users. > > > - If PHP were to add a Map (ordered hash map with null/any > > > scalar/arrays/objects as keys) class type in the future, > > > and that implemented IteratorAggregate, the return type of > > >getIterator() would need something like RewindableKeyValueIterator. > > > - The lack of a relevant datatype is among the reasons why classes such > > > as SplObjectStorage are still an Iterator instead of an > > > IteratorAggregate. (and backwards compatibility) > > > > > > ``` > > > final class KeyValueSequenceIterator implements Iterator { > > > // loop over all values in $values and store a copy, converting > > > // references in top-level array values to non-references > > > public function __construct(iterable $values) {...} > > > public static function fromKeyValuePairs(iterable $entries): self > > >{...} // fromKeyValuePairs([[$key1, $value1]]) > > > public function rewind(): void {...} > > > public function next(): void {...} > > > public function current(): mixed {...} > > > public function key(): mixed {...} > > > public function valid(): bool {...} > > > // and __debugInfo, __clone(), etc. > > > } > > > ``` > > > > The names `RewindableKeyValueIterator` and `KeyValueSequenceIterator` > > are just long-form descriptions of the Iterator API. I don't think we > > need such long names. The name should focus on what it brings. > > > > What it brings is a caching iterator around another iterator, that > > includes re-windability. The SPL provides a `CachingIterator`, but I > > assume it is inadequate somehow (it is basically SPL tradition). Can > > you specifically discuss the shortcomings of `CachingIterator` and how > > you will address them? To that end, have you implemented a > > proof-of-concept for the proposed iterator? > > It caches the results by coercing keys and inserting them into an array, > which is a different use case. > If you rewind a CachingIterator, it rewinds the iterator that it wraps, > which throws for Generators and other types of iterators. > > For RewindableKeyValueIterator, it needs to store keys that can't be used as > array keys. > > I was planning to implement a proof of concept if there wasn't widespread > opposition > and if nobody pointed out the functionality already existed. > > ``` > <?php > function dump_iterable(iterable $x) { > foreach ($x as $key => $value) { > printf("Key: %s\nValue: %s\n", json_encode($key), > json_encode($value)); > } > } > function yields_values(): Generator { yield 0 => 'first'; yield '0' => > 'second'; } > $c = new CachingIterator(yields_values(), CachingIterator::FULL_CACHE); > echo "First CachingIterator iteration\n"; > dump_iterable($c); > var_export($c->getCache()); echo "\n"; // array(0 => 'second') does not > represent that data > echo "Second CachingIterator iteration\n"; > // Fatal error: Uncaught Exception: Cannot rewind a generator that was > already run > dump_iterable($c); > ```
A proof of concept is available at https://github.com/php/php-src/pull/6655 The implementation is similar to SplFixedArray, but instead of an array of `n` zvals, it's an array of `n` pairs of zvals for keys and values. It's an `Iterator` because IteratorAggregator->getIterator() must eventually return an Iterator, but I'm consider also proposing an IteratorAggregate such as KeyValueSequence that can share the same immutable storage (or copy it). For example, `someprefix_map(iterable, callable $mapper): KeyValueSequence` could be useful internally to handle Traversables that could be repeated or use non-scalar keys. Thanks, - Tyson -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php