reduce() in Python

There’s been a reasonable amount of publicity about Guido van Rossum’s comments on the usefulness of the reduce function in Python, but there’s quite an interesting comment on Lambda the Ultimate about how reduce() in Python is quite, well, broken:

Heh. reduce() is broken in python (in that it’s not actually a fold function), so I don’t blame Guido for not being able to figure it out in his head. Rather than handling the zero- and one-element list cases properly, it (a) doesn’t take a basis element, (b) raises an error on empty lists and © RETURNS THE SINGLE ELEMENT WITHOUT CALLING A FUNCTION ON IT in the one-element case (true even in recursion). … The way it’s defined, reduce is not fold, it’s a broken fold.

Folding an empty list results in an error? If that is the case, well, it’s no wonder it wasn’t used in Python very much. I’d very much rather Python rip out a half-arsed fold function altogether rather than trying to make it semi-functional (pun intended). That only gives a bad impression about functional programming, and doesn’t benefit anyone. (Disclaimer: I haven’t double-checked the above claim myself, so I don’t know if what Jacob says it’s true. The Lambda the Ultimate folks usually have their heads screwed on very right when it comes to anything to do with programming language semantics, though …)

For the record, I also disagree with Guido’s assertions that map() and filter() should go away just because list comprehensions exist. map and filter have single purposes: to transform lists, and filter lists. Since list comprehensions can do the job of both map and filter, it’s not immediately obvious what they do, because they now give you more choice, and more choice means less immediate obviousness. If you see a map or filter, you know it’s just doing a list tranformation or a list filter, and that’s it. If you see a list comprehension, you have to read its innards to understand what it’s doing. Or (using Haskell syntax) do you really think [ x | x <- [1..10], isEven x ] more immediately obvious filter (isEven) [1..10]? Considering Python’s heavy emphasis on code readability, that sounds like a step backwards to me.

Update: As pointed out, if you use an initialiser with reduce(), you get sane behaviour back. That’s good. However, I find it a bit odd that an initialiser is optional, rather than mandatory. (It’s managed to fool at least one user into thinking fold is broken — how many more people had the same misconception?)

Update #2: Shane Stephens points out that the optional-initialiser-version of reduce() is actually quite useful. Rather than try to repeat it abysmally, I’ll just clag in a part of Shane’s email here:

… there’s a very good (but possibly non functional-programming-oriented) reason for the optional lack of a basis in python’s reduce function: sometimes you don’t WANT to run the function on a single element. A good example is concatenating strings: reduce(lambda a, b: a + ", " + b, some_array).

Indeed, this is 100% analagous to the foldl1 function in Haskell, which is the same as a fold, but doesn’t take an initialiser element. So, it looks like half of this article of mine is full of crap. :)

blog comments powered by Disqus