
The fate of reduce() in Python 3000 (2005) - tosh
https://www.artima.com/weblogs/viewpost.jsp?thread=98196
======
avian
> Also, once map(), filter() and reduce() are gone, there aren't a whole lot
> of places where you really need [lambda].

By far the most common use of lambda for me is the "key" argument for sort()
or sorted(). As in something like:

foo.sort(key=lambda x:x[2])

Yes, there are itemgetter and attrgetter in operator module, but I always have
to look up their names and writing the lambda expression is simply faster.

~~~
nszceta
itemgetter and attrgetter are compiled C functions called by cpython
[https://github.com/python/cpython/blob/master/Modules/_opera...](https://github.com/python/cpython/blob/master/Modules/_operator.c)

~~~
masklinn
They mean faster to write, not that it's necessarily faster to execute (though
the difference is likely immaterial).

------
malvosenior
> _I think dropping filter() and map() is pretty uncontroversial; filter(P, S)
> is almost always written clearer as [x for x in S if P(x)]_

Performance reasons aside, I _strongly_ disagree with this statement. Anyone
can see that `filter` is going to apply the predicate to the iteratable in the
first statement. The second statement is super awkward, declares a temp var
`x` which to my limited Pythonic eyes looks like it's just calling the
identity function on `x` that comes back from the "filter".

Again, I have no doubt Python is optimized to perform better with the second
form, but that seems a limitation in the interpreter. The syntax to non-Python
devs is pretty raunchy imho.

~~~
Tarean
I am used to haskell's syntax, does this become more readable once you get
used to it? The haskell equivalent would be

    
    
        [x | x <- s, p x]

~~~
roenxi
In many senses this is a style thing, but:

There is going to be a pretty sharp break between people who accept that
working with a collection deserves special syntax support and those who don't.
The readability issue for me isn't that the syntax doesn't look nice, it is
that I've invested years into making a very efficient little mental space that
deals with "data2 = fn(data1)" style constructs and I want to use it here like
I do everywhere else.

Some silly-high percentage of for loops can be cast into this data2 =
fn(data1). Why am I being told that it isn't readable? If this isn't easy to
read, then something is wrong with the function syntax.

A chaining operator, to recast fn3(fn2(fn1(data))) => data > fn1 > fn2 > fn3
with reduce is much more readable, debuggable and easy to reason about than
having to remember what syntax todays language uses to talk about collections
and how that works in to the flow of data in my program.

------
dorfsmay
reduce() is still available in the functools module.

I highly recommend to become familiar with the functools, itertools and
collections modules to anybody using Python and not using them regularly.

~~~
stochastic_monk
I’ve really come to miss reduce in Python’s builtins since std::accumulate has
become a staple in my C++ development. It seems surprising that with Python
3’s increased support for functional concepts, it would relegate reduce to the
standard library.

~~~
masklinn
Not really, increasing support for functional concept was never a goal of
Python 3 (or at all), it was mostly incidental and it did regress in other
ways than just shoving the (relatively little used due to the existence of
min/max/any/all) out of the builtins e.g. while extended unpacking was added,
unpacking iterable function parameters was removed.

------
sifoobar
I spent some time comparing Python's [0] map/filter and comprehensions against
Snigl [1].

Comprehensions are just plain faster, especially if you perform say filter and
map at the same time.

That and a lambda implementation that only a mother could love, lack of tail
call optimization etc.

What bothers me about comprehensions is they come with a separate set of
rules, like LOOP in Common Lisp or templates in C++; but I'm pretty sure the
restrictions are part of what makes them fast.

[0]
[https://gitlab.com/sifoo/snigl/blob/master/bench/seq.py](https://gitlab.com/sifoo/snigl/blob/master/bench/seq.py)

[1]
[https://gitlab.com/sifoo/snigl/blob/master/bench/seq.sl](https://gitlab.com/sifoo/snigl/blob/master/bench/seq.sl)

~~~
klyrs
Agreed, this made my eye twitch:

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

sure, Guido, if you think it's clear that the variable x is in the enclosing
scope...

~~~
joshuamorton
Python 3 changed this, iirc. Comprehension no longer leak.

~~~
jks
It's still an assignment operation, and you can still get weird effects in the
enclosing scope. I did something like this by accident:

    
    
        Python 3.7.1 (default, Nov 28 2018, 21:47:25)
        Type 'copyright', 'credits' or 'license' for more information
        IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.
    
        In [1]: lookup = {}
    
        In [2]: [x for (x, lookup[x]) in ('ab', 'cd', 'ef') if x in lookup]
        Out[2]: ['a', 'c', 'e']
    
        In [3]: lookup
        Out[3]: {'a': 'b', 'c': 'd', 'e': 'f'}

~~~
joshuamorton
The whacky thing there isn't the list comprehension though, it's the implicit
assignment of `lookup[x]`. If you desugar it some (which isn't really
possible, but

    
    
        [(x, lookup.setdefault(x, y)) for (x, y) in ('ab', 'cd', 'ef')]
    

comes close), what's happening makes a lot more sense. It works exactly the
same way as any other scope: first checks locally, then nonlocally or
globally. You're modifying a nonlocal. The syntax you're using to modify the
nonlocal is surprising, but that's the surprising part, not the ability to
modify a nonlocal.

~~~
theamk
I'd desugar it like this:

    
    
        >>> lookup = {}
        >>> for (x, lookup[x]) in ('ab', 'cd', 'ef'):
        ...     pass
        ... 
        >>> lookup
        {'a': 'b', 'c': 'd', 'e': 'f'}
    

This makes is much more clear that there is just a double assignment, and that
comprehension are pretty close to regular `for` statement.

(this was a great example btw -- I write python3 a lot, and I was staring at
it for a long time before Icould figure it out)

------
dang
Discussed in 2008:
[https://news.ycombinator.com/item?id=225764](https://news.ycombinator.com/item?id=225764)

------
wodenokoto
Why were map and filter kept? What can you do that cannot be as easily done
with generator expressions?

~~~
studentik
Coconut has many examples of functional programming techniques that cannot be
easily done with python:
[https://coconut.readthedocs.io/en/master/DOCS.html](https://coconut.readthedocs.io/en/master/DOCS.html)

Strangely FP is not popular among python developers.

~~~
mlevental
is coconut used in production? seems like a really bad idea?

~~~
studentik
It's python after all, so why not?

------
taraploughman
Does anyone know the origin story of reduce/filter in Python? It seemed
interesting. I remember watching a talk by Guido. He mentioned that someone
submitted a PR with it, and that it was very complete, so he merged it. But
I'm curious what the decision making process was like for Python back then, or
if there even was a formal process.

Programming language archaeology is a field that ought to get more attention.

