Python comprehensions in particular are unfortunate because the flow of the query, like in SQL, is not linear - the projection part is written first, but happens last. Thus, reading a non-trivial query requires moving back and forth to resolve the references.
Compare that to C# LINQ, which is also a kind of sequence comprehension. That one not only has linear syntax where the data flow in the query is strictly left to right, but it's explicitly defined by mapping it to functional pipeline style. Thus, it ends up being a strict improvement on the latter.
(Another example of "good" sequence comprehensions in this sense is XQuery FLWOR)
Chained pipelines (`foo.filter(...).map(...)`) are fine to me, certainly more verbose then comprehensions for better & worse, but I get lost in pipelines expressed as nested functions (`map(filter(a, ...), ...)`).
If its more than a very simple expression, with one simple for, and no if or very simple if, I try to use at least (more, with parens, if necessary): one line for the result expression, one line for each for clause, and one line for each if clause.
But, while the boundary is subjective, it doesn’t take much before functional pipeline style is clearer (Python lambdas being more limited and syntacticaly cumbersome, functional pipeline style isn’t as clean in Python as it is in other languages, though.)
heads :: [[a]] -> [a]
heads xs = [y | (y:ys) <- xs]
-- heads [[1],[],[2,3]] == [1,2]
Doing the above using maps and filters looks very different: heads' = map head . filter (not . null)
Python doesn't really support Haskell-like pattern matching, so the above doesn't apply.It does as of 3.10:
https://peps.python.org/pep-0622/
Not as sophisticated as what Haskell can do, I'm sure, but I think it's a good addition to Python.
(a -> Maybe b) -> [a] -> [b]
Which yields mapMaybe: https://hackage.haskell.org/package/base-4.17.0.0/docs/Data-.... so you end up with heads = mapMaybe safeHead [
if List.isEmpty items then
p “No items”
else
ul
[
for item in items do
li [ item ]
]
]if len(items) == 0: return [p("no items")] else: ul( [ li(item) for item in items ] )
I am also not a fan of convoluted list comprehension (in python) so powerful is not necessarly a good thing.
And consider this example:
[
h1 “items”
if List.isEmpty items then
p “No items”
else
ul
[
for item in items do
li [ item ]
]
]I don’t see how this can be done in Python without using mutation, which makes code scattered across multiple statements