On Thu, Apr 4, 2019, at 10:46 PM, Stephen Reay wrote:
> > Discussion:
> >
> > For me, the inability to work with arrays is the big problem with the
> > second approach. I very very often am type declaring my returns and
> > parameters as `iterable`, which means I may have an array and not know it.
> > Using approach 2 means I suddenly really really need to care which kind of
> > iterable it is, which defeats the purpose of `iterable`. Calling methods
> > on arrays, though, I'm pretty sure is out of scope.
> >
> > Frankly were it not for that limitation I'd say I favor the chained method
> > style, as while it is more verbose it is also more self-documenting. Given
> > that limitation, I'm torn but would probably lean toward option 1. And of
> > course there's the "methods that apply to all traversable objects" thing
> > which is its own can of worms I know nothing about.
> >
> > (If someone has a suggestion for how to resolve that disadvantage, I'd love
> > to hear it.)
> >
> > Those seem like the potential options. Any further thoughts? Or
> > volunteers? :-)
> >
> > --Larry Garfield
> >
> > --
> > PHP Internals - PHP Runtime Development Mailing List
> > To unsubscribe, visit: http://www.php.net/unsub.php
> >
>
> (Sorry, sent from wrong address, sending again!)
>
> Hi Larry,
>
> I’ve mostly ignored this thread until now - I find a lot of the
> “shorter syntax” (i.e. the short closures RFC) to sound a lot like the
> arguments “I don’t like semicolons/it has to be ‘pretty'” that happen
> in other language communities.
In defense of terse syntax, it's not a question of "pretty". It's a question
of making it feasible to operate at a higher level of abstraction. Really,
generators didn't offer much of anything that couldn't be done by defining and
building an Iterator-implementing class. They're "just" syntactic sugar.
However, they allow the developer to conceptualize a problem in a different
way, and most of the machinery then falls away. That means I can now think in
terms of "call this function, then iterate the stream it gives me back" and
within the function I can just have normal logic with `yield` floating around
as needed. Anything I do there *could* be done with an Iterator class; I've
done it some weird things with Iterators before. But the ability to think in
terms of an ad-hoc stream of values really changes the way you think about the
problem, and in a very good way.
Similarly, short closures isn't about "let's make functions easier to write".
That's a side effect. They should be thought of more as a way to easily
encapsulate "apply this expression to this set of values". So the advantage is
not that
$y= 5;
array_map(fn($x) => $x*$y, $arr);
is less typing than
array_map(function ($x) use ($y) {
return $x * $y;
});
It's that in the first option you don't think about it as a function, you think
about it as an expression applied over a set. That's a higher-order mental
operation, and once you start doing that you can conceptualize the program in a
different, more higher-order, less bug-prone way.
Just like there's nothing you can do with foreach() that you can't also do with
for()... but foreach() lets you think in terms of "just do it to everything"
rather than think in terms of the machinery of iteration.
I see comprehensions the same way. At one level they're "just" short syntax
for generators, but they're more about making it possible to reason about your
logic at a higher level, in a more declarative fashion.
(There's probably a conference talk in there somewhere, from for to foreach to
iterators to generators to comprehensions, each coming up one level of
abstraction.)
> But the first example you give here, I can see the logical approach -
> as you say, it’s a currently-valid foreach statement, wrapped in square
> brackets. Would it have to be a single line to parse, or could it be
> wrapped when the condition gets longer (yes I know it could just become
> a regular generator then, I’m just wondering about what happens when
> someone adds a new line in there (in a language that historically
> doesn’t care about newlines)
The RFC specifically says whitespace is irrelevant. If you want to break a
comprehension across multiple lines, you do you. But if it's getting large
enough that it's ugly to read that way it's a good sign you may want to take a
different approach. (A defined function with real foreach statements, multiple
defined comprehensions that reference each other, etc.)
> I like the second concept a lot too, but how would this cope with for
> example: a userland class implements iterator but *also* defines a
> `filter(callback $fn): self` method for the exact same purposes were
> discussing. How is that handled?
I have no idea at the moment. :-) That would be a possible BC issue. My first
thought is that if an iterator defines filter(), map(), etc. itself then it's
overriding the default behavior and can do what it wants, but there's also
possible function signature mismatches there. It may just have to be a BC
break in those cases. I am open to alternate suggestions. (That may push it to
PHP 8, which would be unfortunate but if that's the way it goes, that's the way
it goes.)
On Fri, Apr 5, 2019, at 3:41 AM, Michał Brzuchalski wrote:
> Hi Larry,
>
> pt., 5 kwi 2019 o 03:55 Larry Garfield <[email protected]> napisał(a):
>
> >
> > Advantages:
> >
> > * Very compact.
> > * Works for both arrays and traversables
> > * Would play very nicely with the proposed spread operator for iterables (
> > https://wiki.php.net/rfc/spread_operator_for_array).
> >
>
> IMO not nicely cause spread operator in current proposal raises an error on
> key preserved traversable[1].
> This means example like below would fail
>
> $a = ['foo' => true, ...[foreach($_GET as $key => $value) yield $key =>
> $value]]; // error
>
> [1] https://wiki.php.net/rfc/spread_operator_for_array#string_keys
True; it's not a complete solution. Per the thread on spread string keys may
make a comeback. But I was thinking more of a case of:
$arr = ...[foreach ($list as $k => $v) yield $k => $v];
For those cases where you really do want an array to operate on next, rather
than a generator. If you're in a situation where ... doesn't work,
iterator_to_array() still does; it's just more verbose.
--Larry Garfield
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php