Sara and I tried putting the expression first, as in Python, but that made the lexer very unhappy
There is another possible reason to put the expression last: It
preserves the ordering of the intended foreach loop. What I mean to say
is this: One can change a loop into a comprehension by simply removing
the newlines, braces, etc. Other than that, the various elements stay in
the same place. For me, this makes it easier to 'see' the intended loop
in the comprehension. I have always found the Python syntax to be
confusing in this regard.
I've brought up a similar proposal a few years ago, probably around the
time generators were first introduced: https://externals.io/message/61021
May I please point at one particularly nice detail in Nikita's proposal:
A dual syntax for both array comprehensions and generator expressions:
$arr = [foreach ($users as $user) yield $user->firstName]
$gen = (foreach ($users as $user) yield $user->firstName)
I would love to see a dual syntax included in the current proposal as
well. I think it makes sense considering the fact that many functions in
PHP only accept arrays, not generators. So in practice, I expect that
using comprehensions as proposed in the new RFC will also require doing
a lot of iterator_to_array()
. A dual comprehension syntax could fix that.
One little remark about the array comprehension syntax: Using a keyword
other than "yield" would make the difference between the two more
obvious. Perhaps the array comprehension could use "return":
$arr = [foreach ($users as $user) return $user->firstName]
$gen = (foreach ($users as $user) yield $user->firstName)
I think my main point of feedback would be to stick closer to existing PHP
syntax, even if it costs us some brevity. I would prefer
$gen = [foreach ($list as $x) if ($x % 2) yield $x * 2];
over
$gen = [for $list as $x if $x % 2 yield $x * 2];
The latter is nice in languages that generally use "for" for this purpose
and generally allow omitting parentheses, but I don't think it's good to
introduce this kind of syntax inconsistency in one place.
Keeping the parenthesis would seem to be more consistent indeed.
Omitting the curly brackets of a "foreach" loop is valid syntax in
current PHP while omitting the parenthesis from the "foreach" and "if"
is not.
However, the RFC also points out that the syntax should be limited
compared to a "foreach" loop, which makes sense. This makes me want to
favor the syntax as proposed, without the parenthesis. The "full syntax"
with parenthesis kind of suggests that comprehension syntax is no
different than a regular foreach loop, supporting the existing if / else
syntax, etc, but on a single line. That level of similarity might be
confusing. For example, if I can write:
if ($x % 2) yield $x * 2;
and
if ($x % 2) yield $x * 2; else yield $x;
then why is it that I can write:
[foreach ($list as $x) if ($x % 2) yield $x * 2]
but not:
[foreach ($list as $x) if ($x % 2) yield $x * 2; else yield $x]
Bottom line: If comprehensions aim to provide an alternative syntax to
foreach loops, with different rules, why try to make them look exactly
like foreach loops? That could be confusing.
Also note that Python list comprehensions basically show the same
"inconsistency" as the proposed PHP comprehensions: In PHP, parenthesis
are required after "foreach" or "if". The RFC proposes to omit them in
comprehensions. In Python, colons are required after "for" and "if".
Python comprehensions omit these. Not that "Python has it too" is a
valid argument for anything at all though...
I do agree that using "foreach" in stead of "for" may be a better
choice. The proposed comprehensions are compressed foreach loops. In my
mind, I want to think "foreach" when I see a comprehension, not "for".
One more thing that kinda "annoys" me / what I base my above statement
on is; when reading some of the example is that the code examples
given, as in the code inside a Comprehension does not look very
PHP-ish besides the $-sigil and I do not like the idea of having "two"
syntaxes for PHP where one is only available in a very narrow-,
specific context and I feel it would be prone to potential errors.
I fully understand this feeling. It's new to PHP and looks quite weird
at first. Many of us know comprehensions from the Python world. Maybe
the association with Python is so strong that the very concept of
comprehensions feels Pythonic and not something that fits into PHP.
I was skeptical about comprehensions myself when I started coding in
Python. However, after getting used to them, I must admit that I find
comprehensions easier to read than traditional loops. When they are
short, that is. :) Looking at a comprehension, I can still 'see' the
intended loop. Only quicker, because there is less code to scan.
I would really like to encourage trying to step over that initial
gosh-that's-weird feeling and consider what it could offer for PHP.
Look at this example from the RFC:
$result = (function() use ($table) {
foreach ($table as $num => $row) {
if ($num % 2 == 0) {
foreach ($row as $col => $val) {
if ($col >= 3) {
yield $num => $val;
}
}
}
}
})();
We could use this syntax today:
$result = (function () {
foreach ($table as $num => $row)
if ($num % 2 == 0)
foreach ($row as $col => $val)
if ($col >= 3)
yield $num => $val;
})();
Compare that to this code using comprehensions, using the same whitespace style:
$result = (for $table as $num => $row
if $num %2 ==0
for $row as $col => $value
if $col >= 3
yield $num => $val
);
In my opinion there are no meaningful differences here, and nothing
prevents you from using the all-in-one-line style if you care to.
For bigger loop constructs, the advantage of comprehensions is indeed
less obvious. In Python code, I find myself using comprehensions to
replace mostly simple loops. That is where they really shine I think.
Omitting curly brackets and using the all-in-one-line style is indeed
possible, which closes the gap with closures even further. However, in
practice the brackets are typically enforced by code style checkers, for
good reason. In comprehensions, they can be safely omitted.
Oof, that sucks and I didn't even have to bind any variables. But if
we had shorter closures (this is JavaScript style) then it's fine
again:
$result = array_map(($num) => intval($num, 16), $hex_numbers);
My point is, closures are the major culprit. We really need shorter closures.
That is one great example of how short closures might do the job.
However, this assumes that what you need as output is an array. As
proposed in the RFC, comprehensions would be generators. In the case
where the above example would need to create a generator in stead of an
array, a comprehension would be preferable over a closure.
I think both short closures and comprehensions have great potential, I
don't think we should pick one or the other.
Also agree about parentheses - reading a complex expression without
separators would be annoying. I know python loves to do this but even in
an IDE with highlighter it's not always easy to quickly figure out which
part belongs where. Delimiting it would make it easier IMHO, and also
allows again to carry over the intuition of how foreach() and if() work
from already known constructs.
Only, the intuition of how "foreach" and "if" works does not fully apply
due to the limited syntax of comprehensions. For example, you can't pair
the "if" with an "else" in the current proposal. Maybe that could be
supported as well, but that is not my point. If comprehensions start
looking like familiar foreach loops but do not work the same way, would
that not be confusing?
Kind regards,
Dik Takken