Re: RFC Draft: Comprehensions
| From: | Larry Garfield | Date: | Thu, 21 Mar 2019 15:22:50 +0000 |
| Subject: | Re: RFC Draft: Comprehensions | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-104857@lists.php.net to get a copy of this message | ||
On Wed, Mar 20, 2019, at 5:44 PM, Rowan Collins wrote:
> On 20/03/2019 20:39, Stanislav Malyshev wrote:
> > Hi!
> >
> >> It's not that you can't make an array into a generator, but you can't
> >> make
> >> an eagerly-evaluated expression into a lazily-evaluated one.
> > Not sure what you mean here.
>
>
> I mean that, given a syntax that lazily-evaluates something, you can
> "fast-forward" the result to make it eagerly-evaluated; given a syntax
> that eagerly-evaluates something, you cannot do the opposite. Therefore,
> a lazy-evaluating syntax is more powerful, in that it can do everything
> an eager-evaluating one can do *and more*.
This is really the crux of the array-vs-generator question for comprehensions.
No one disputes that both arrays and generators have their good use cases. No one disputes that
there are cases where one is favorable over another. I argue that in *most* cases where you'd
be using a comprehension it wouldn't actually matter in practice which one it is. Which one is
more prevalent in practice depends who the developer is; I personally use generators a ton because I
find them easier to work with syntactically when processing complex data structures regardless of
their size, but others have their own styles.
It comes down to conceptual simplicity. A greedy-evaluated list can be trivially composed from a
lazy-evaluated list and a "fast-forward" operator. Both of those are useful in their own
right, and can be composed to produce a greedy evaluated list. However, given a greedy-evaluated
list there is no way to produce a lazy-evaluated list at all. Thus we will need a lazy-evaluated
list either way (and that's my goal with comprehensions).
Given a lazy-evaluated list and a fast-forward operator... an alternate greedy-evaluated list is a
nice-to-have, unless it creates more syntactic complexity and/or confusion. As an unrealistic
Pythonic example, were we to do:
{foreach $foo as $bar yield $bar*2} // gives a generator
[foreach $foo as $bar yield $bar*2] // gives an array
Then we're introducing confusion for users who have to learn two different very-similar-looking
syntaxes, and we're laying claim to both []-wrapped expressions and {}-wrapped expressions to
mean something.
OTOH, if we just have the one syntax:
[foreach $foo as $bar yield $bar*2] // gives a generator
And include a nicer "fast-forward" operator than interator_to_array(), then we
automatically get:
...[foreach $foo as $bar yield $bar*2] // turns the generator into an array on the fly
Which is both easier to learn (two independently useful primitives, composed), easier to understand
(because the latter is not as visually similar), and doesn't lay claim to two different
potentially useful pieces of block syntax, leaving the other open for later use by some other idea
later.
(And if we don't introduce a new fast-forward operation, then iterator_to_array() is already
still there and while a bit more verbose than we'd like it certainly doesn't the job just
as effectively.)
Also, I reiterate that in the majority of use cases I see where a comprehension would be used (not
all, but most) the result would end up in a foreach() loop, or returned as an iterable and thus
whether it's an array or generator won't actually matter.
So really, "I don't use generators much" is not a meaningful argument. A
lower-overhead implementation (both for the user and the parser) is the argument, and on that front
"generator only" wins, with "both" a weak compromise position that doesn't
add much. "Array only" is by far the weakest result.
Now can we go back to bikeshedding the actual syntax style to use, per my earlier email? :-)
--Larry Garfield