Hacker Timesnew | past | comments | ask | show | jobs | submitlogin

I have the same thoughts about list comprehension. The first time I read this quote I thought Guido was being ironic:

    "filter(P, S) is almost always written clearer as [x for x in S if P(x)]"


the comprehension version is actually clearer, though. consider: in filter(P, S)

1. which is the predicate and which is the list?

2. is the list filtered in place (destructively) or is a new list emitted?

3. does filter mean "select when P" or "reject when P"? both are valid readings of the english word 'filter', with common non-scitech usage actually leaning more towards 'filter out'

the list comprehension has none of those ambiguities.


To be explicit, they're clearer _in Python_ due to the prevailing style and sensibilities of the community. It's possible to adopt a different set of guidelines that would eliminate the ambiguity.

For instance, in Haskell the data you're working with is always the last argument. This stems from the way currying works, which makes life much more pleasant if the predicate comes first. Ruby also makes it clear, as there's special syntax for passing an anonymous function to a function call.

For your second point, pure functional languages have complete clarity in this sort of thing. In Haskell it's obvious that you're returning a new list, and you couldn't mutate the original even if you wanted to. Ruby has a convention where ambiguously-mutating function names are suffixed with "!" to indicate that this version mutates.

As for your third point, I can see where you're coming from, but filter is a venerable function name. There's a version of filter in most functionally inspired languages (and a version of map, reduce, etc.) and all the ones I've seen only keep elements that match the predicate. I suppose this may be why Ruby calls it "select" instead of "filter". There's also an inverted version called "reject". All of those also have an in-place variant (select!, reject!, map!).

My point is that the ambiguity is not unavoidable. These things could easily be made clearer with different conventions/language-support. Other languages do quite well with these tools, so it obviously can be done in a clear way.


The C#/LINQ syntax for this is nice for this reason. It's S.Where(P), which makes it clear that the thing on the left is the thing being filtered, and that it's taking values x where P(x) is true.

One could also make the argument that it also suggests that it's a non-destructive operation, but that might just be years of familiarity with SQL semantics talking.


Python 3 really pushes you towards list comprehensions as you have to wrap your filter in a list(). I ended up just going with the flow. However, nesting list comprehensions can get me into a place where I forget what it is I'm doing.


This was the worst thing about switching to Python 3. `map` and `filter` are annoyingly "lazy" and `reduce` is basically shunned.

I wonder if the real reason this was changed was to discourage the use of `map` and `filter`... wrapping them each time with `list` is a pain in the ass and hurts readability.

And it breaks code in hard to anticipate places if you don't.

First thing I do when starting something is change them back in global scope. If I want a generator, I'll write one...

Down with PEP 8!


I don't think "lazy" is a problem in real code. They just annoys when in interactive shell which you just want to print their result. In fact python3 has changed a lot of list like data type or function to "lazy", zip(),dict.items() etc. IMO this helps a lot to reduce memory usage. Thus more efficient with functional programming


I think Guido makde it very clear he does not favour any use of map/reduce.


The latter example certainly is clearer.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: