"Kicking and screaming", to be fair, is a bit of a literary exaggeration. But Rossum did not particularly want to add functional features to the language and only did so at the behest of his users. Lambda as well as map/filter/reduce were "a significant, early chunk of contributed code", not something he intended the language to have from the outset[1]. More recently, he has been trying to deemphasize these features in favor of list comprehensions.
Moreover, Rossum is opposed to proper tail calls and even recursion in general[2]. He also doesn't like folds, and generally thinks you should just express that sort of logic as some sort of loop. (I forget exactly where I read that opinion, but it could easily have been [1].)
He's a staunchly imperative programmer, and the design of Python shows this off consistently except for an initial accident of first-class functions and closures (of a sort)—a design he likes because it enabled other, non-functional language features like new-style classes.
"Kicking and screaming" might have been an exaggeration, but it captures his overall attitude towards functional programming pretty well.
There are many thing he didn't intend from the outset, so that by itself isn't so significant. Some other things he didn't intend were the type/class dichotomy and the inability to raise an exception from comparison. Two changes that come from external users include David Ascher's rich comparisons and Samuele Pedroni's proposal to use the C3 method resolution order.
> "Steven Majewski might not agree with me, but in Python you are better
off using "for" loops (the overhead of calling a function for each
element is quite substantial)."
You can see that's still true with CPython:
% python -mtimeit -c 'x=range(100)' 'd=[]' 'for a in x: d.append(a*a)'
10000 loops, best of 3: 22.1 usec per loop
% python -mtimeit -c 'x=range(100)' 'map(lambda a: a*a, x)'
100000 loops, best of 3: 18.2 usec per loop
% python -mtimeit -c 'x=range(100)' 'd=[];ADD=d.append' 'for a in x: ADD(a*a)'
100000 loops, best of 3: 12.7 usec per loop
% python -mtimeit -c 'x=range(100)' '[a*a for a in x]'
100000 loops, best of 3: 9.91 usec per loop
FWIW, Python has made at least one language change in order to improve performance. In Python 1.x the following was possible:
def f(x):
from math import *
return cos(x*sin(x))
Python 1 used a dictionary for locals, while Python 2 introduced a pre-allocated array of variables for locals so lookup could be done via a simple index offset.
In the same email, van Rossum writes:
> "To be honest, I wish I hadn't introduced lambda, map, filter and reduce -- they support a style that is inconsistent with the rest of Python. Unfortunately, in the sake of backward compatibility, I can't take them out. So enjoy them if you have to. But don't get too thrilled!"
That same email also says:
> Ah, the infamous "first class objects" argument again. Really, I don't understand all the fuzz about lambda, and I don't see why its introduction made functions any more first class than they already were in Python. They are entirely syntactic sugar for local function definitions. Everything you can do with lambda you could always do with local functions, at the cost of one local temporary identifier -- surely no big deal!
This is interesting because you mentioned "initial accident of first-class functions". Why do you think first-class objects are an "initial accident", and not a deliberate decision?
when you have a project concerned with readability, adding new features is a huge community cost in the long run because it's one more thing you have to learn to deal with as you go across projects.
One of the wonderful things with python is that you can dig into the code of most python projects and not have to worry much about completely different idioms requiring a complete context switch to understand how they work.
He is not really being black and white, he is doing a service by preventing language bloat ala C++ as much as possible.
It always struck me as a reaction against Perl's "Tim Toady" approach, encouraging a more idiomatic and unified programming strategy to ease comprehension and teaching (IIRC the origin of Python, or at least ABC).
This is exactly what makes Python a bad language. Lack of expressive power results in an unnecessary complexity of the code. It's much easier to comprehend a short, compact code written in the familiar, domain-specific idioms rather than digging through convoluted pile of low-level leaky idioms like loops, list comprehensions, generators, classes, recursion and all that irrelevant crap.
Just wanted to add my voice: this matches exactly my perception. I used to really like Python, but I've come to the conclusion that there's a certain point in terms of the complexity of a Python program where everything just blows up and it's next to impossible to keep it from becoming a tangled mess. This point is reached incredibly soon, something like five classes with some inheritance, some interface/virtual/abstract class. Ugh. Now I avoid Python as much as possible, much preferring C++ or Java, which I think work incredibly better for larger programs.
I used to think I needed both, but since picking up Haskell I don't miss for loops at all and I rarely need to use explicit recursion either--now I almost exclusively use higher-order functions like `map`.
When I started learning Python, I always used map and filter because they were just how I thought about problems. But there is a certain bit of elegance to being able to write
[process(child) for child in node if child is not None]
in lieu of
map(process, filter(notNone, child))
The functional style makes better use of conceptual primitives, but the imperative style is generally better for documenting.
for child in node:
if child is not none: # Comment explaining decision.
process(child) # Comment explaining more stuff.
Oh, I agree. I mentioned it because is the preferred style in Python -- probably because it is so readable. But my points still apply to the loop version.
I'm actually a bit scared of using recursion in production (at a web company). It has its place of course, but gratuitous recursive code that could have been written without gives me pause. It's too easy to miss an edge case and end up with a stack overflow, and that can bring down a whole server farm.
changeset: 1369:89e1e5d9ccbf
branch: legacy-trunk
user: Guido van Rossum <guido@python.org>
date: Tue Oct 26 17:58:25 1993 +0000
summary: * compile.[ch]: support for lambda()
so that's only two years after it was first released, and before it was 1.0. The code was user-contributed, but given the timing and the recollections at http://python-history.blogspot.com/2009/04/origins-of-python... I can't agree that the historical record is compatible with "dragged kicking and screaming to lambda."
What a shame it would be if he'd removed it. Pyspark now makes great use of lambdas, and I'd strongly argue that having to name every function argument would be strictly worse.
The loose consensus in Python is that lambdas are best when they fit on a line, which is what the examples at http://www.mccarroll.net/blog/pyspark2/index.html do. Otherwise, make a named function.
I think arguments like this are one of the main reasons why van Rossum has kept lambda in the language.
I'm not sure that's true, anyone have more details? Most people don't like the parens everywhere with lisp, but otherwise speak of it highly.
Edit: thanks for the links on lambda, read all of them. However, didn't see anything obvious written about lisp itself.