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 <la...@garfieldtech.com> 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

Reply via email to