Python syntax in Lisp and Scheme

Alex Martelli aleax at aleax.it
Sat Oct 4 13:02:41 EDT 2003


Grzegorz Chrupala wrote:
   ...
>>  >>> def foo(n):
>>  ...     box = [n]
>>  ...     def foo(i): box[0]+=i; return box[0]
>>  ...     return foo
>>  ...
> 
> It's still a hack that shows an area where Python has unnecessary
> limitations, isn't it?

Debatable, and debated.  See the "Rebinding names in enclosing
scopes" section of http://www.python.org/peps/pep-0227.html .

Essentially, Guido prefers classes (and instances thereof) to
closures as a way to bundle state and behavior; thus he most
emphatically does not want to add _any_ complication at all,
when the only benefit would be to have "more than one obvious
way to do it".

Guido's generally adamant stance for simplicity has been the
key determinant in the evolution of Python.  Guido is also on
record as promising that the major focus in the next release
of Python where he can introduce backwards incompatibilities
(i.e. the next major-number-incrementing release, 3.0, perhaps,
say, 3 years from now) will be the _elimination_ of many of
the "more than one way to do it"s that have accumulated along
the years mostly for reasons of keeping backwards compatibility
(e.g., lambda, map, reduce, and filter, which Guido mildly
regrets ever having accepted into the language).


> As Paul Graham says (<URL:http://www.paulgraham.com/icad.html>):
> 
>> Python users might legitimately ask why they can't just write
>> 
>> def foo(n):
>>   return lambda i: return n += i

The rule Python currently use to determine whether a variable
is local is maximally simple: if the name gets bound (assigned
to) in local scope, it's a local variable.  Making this rule
*any* more complicated (e.g. to allow assignments to names in
enclosing scopes) would just allow "more than one way to do
it" (making closures a viable alternative to classes in more
cases) and therefore it just won't happen.  Python is about
offering one, and preferably only one, obvious way to do it,
for any value of "it".  And another key principle of the Zen
of Python is "simple is better than complex".

Anybody who doesn't value simplicity and uniformity is quite
unlikely to be comfortable with Python -- and this should
amply answer the question about the motivations for reason
number 1 why the above foo is unacceptable in Python (the
lambda's body can't rebind name n in an enclosing scope).

Python draws a firm distinction between expressions and
statements.  Again, the deep motivation behind this key
distinction can be found in several points in the Zen of
Python, such as "flat is better than nested" (doing away
with the expression/statement separation allows and indeed
encourages deep nesting) and "sparse is better than dense"
(that 'doing away' would encourage expression/statements
with a very high density of operations being performed).

This firm distinction should easily explain other reasons
why the above foo is unacceptable in Python: n+=i is a
statement (not an expression) and therefore it cannot be
held by a 'return' keyword; 'return' is a statement and
therefore cannot be in the body of a 'lambda' keyword.

>>  or even
>> 
>> def foo(n):
>>   lambda i: n += i

And this touches on yet another point of the Zen of Python:
explicit is better than implicit.  Having a function
implicitly return the last expression it computes would
violate this point (and is in fact somewhat error-prone,
in my experience, in the several languages that adopt
this rule).

Somebody who is unhappy with this drive for explicitness,
simplicity, uniformity, and so on, cannot be happy with
Python.  If he wants a very similar language from most
points of view, BUT with very different philosophies, he
might well be quite happy with Ruby.  Ruby does away with
any expression/statement distinction; it makes the 'return'
optional, as a method returns the last thing it computes;
it revels in "more than one way to do it", clever and cool
hacks, not perhaps to the extent of Perl, but close enough.

In Ruby, the spaces of methods and data are separate (i.e.,
most everything is "an object" -- but, differently from
Python, methods are not objects in Ruby), and I do not
think, therefore, that you can write a method that builds
and returns another method, and bind the latter to a name --
but you can return an object with a .call method, a la:

def outer(a) proc do |b| a+=b end end

x = outer(23)
puts x.call(100)   # emits 123
puts x.call(100)   # emits 223

[i.e., I can't think of any way you could just use x(100)
at the end of such a snippet in Ruby -- perhaps somebody
more expert of Ruby than I am can confirm or correct...?]
but apart from this it seems closer to what the above
quotes appear to be probing for.  In particular, it lets
you be MUCH, MUCH denser, if that is your purpose in life,
easily squeezing that outer function into a (short) line.
Python is NOT about making code very dense, indeed, as
above mentioned, it sees _sparseness_ as a plus; a typical
Pythonista would cringe at the density of that 'outer'
and by contrast REVEL at the "sparsity" and "explicitness"
(due to the many names involved:-) of, e.g.:

def make_accumulator(initial_value):
    accumulator = Bunch(value=initial_value)
    def accumulate(addend):
        accumulator.value += addend
        return accumulator.value
    return accumulate

accumulate = make_accumulator(23)
print accumulate(100)   # emits 123
print accumulate(100)   # emits 223


(using the popular Bunch class commonly defined as:
    class Bunch(object):
        def __init__(self, **kwds):
            self.__dict__.update(kwds)
).  There is, of course, a cultural gulf between this
verbose 6-liner [using an auxiliary class strictly for
reasons of better readability...!] and the terse Ruby 
1-liner above, and no doubt most practitioners of both
languages would in practice choose intermediate levels,
such as un-densifying the Ruby function into:


def outer(a)
  proc do |b|
    a+b
  end
end

or shortening/densifying the Python one into:

def make_accumulator(a):
    value = [a]
    def accumulate(b):
        value[0] += b
        return value[0]
    return accumulate

but I think the "purer" (more extreme) versions are
interesting "tipizations" for the languages, anyway.


Alex





More information about the Python-list mailing list