[Python-ideas] Rehabilating reduce (as "fold")

Joshua Landau joshua at landau.ws
Fri Jul 12 09:52:16 CEST 2013


On 12 July 2013 07:01, Nick Coghlan <ncoghlan at gmail.com> wrote:
> The strange contortions of the "fast sum for lists" discussions got me
> wondering about whether it was possible to rehabilitate reduce with a less
> error-prone API. It was banished to functools in 3.0 because it was so
> frequently used incorrectly, but now its disfavour seems to be causing
> people to propose ridiculous things.
>
> The 2.x reduce is modelled on map and filter: it accepts the combinator as
> the first argument, and then the iterable, and finally an optional initial
> value. The most common error was failing to handle the empty iterable case
> sensibly by leaving out the initial value, so you got a TypeError instead of
> returning a result.
>
> So, what if we instead added a new alternative API based on Haskell's "fold"
> [1] where the initial value is *mandatory*:
>
>     def fold(op, start, iterable):
>         ...
>
> Efficiently merging a collection of iterables into a list would then just
> be:
>
>     data = fold(operator.iadd, [], iterables)
>
> I'd personally be in favour of the notion of also allowing strings as the
> first argument, so you could instead write:
>
>     data = fold("+=", [], iterables)
>
>
> This could also be introduced as an alternative API in functools.
>
> (Independent of this idea, it would actually be nice if the operator module
> had a dictionary mapping from op symbols to names, like
> operator.by_symbol["+="] giving operator.iadd)

This sounds like a good idea to me (although by_symbol could well have
a more catchy name) -- there are so many places where "lambda a, b: a
+ b" is just ugly. This could work itself into a lot of APIs. What
would be the equivalent for "operator.pos", "operator.neg" and
"operator.{get|set|del}item"?

...But then I start suggesting extensions like having operator.* auto
curry: "operator.iadd(left=foo)(bar)" === "operator.iadd(foo, bar)"
(currying should require separate keyword-only syntax, and those
keywords always make a curried function). And uncurry -- we need that
if we want this to work with map.


More information about the Python-ideas mailing list