What does Python fix?

Alex Martelli aleax at aleax.it
Sat Jan 19 13:33:54 EST 2002


François Pinard wrote:

> [Jason Orendorff]
> 
>> I've tried to translate the Scheme into roughly equivalent Python,
>> to see if it's really longer.  Take a look.
> 
> Thanks for the exercise, Jason.
> 
>> def modify(redo, undo, focuses):
>>     def redo_function():
>>         redo()
>>         return undo_function, focuses
>>     def undo_function():
>>         undo()
>>         return redo_function, focuses
> 
> This might not work, I'm not sure.  Take `undo_function', in the `return'
> statement within `redo_function': it is neither global nor local, so I
> guess it might not be defined.  Don't we need to resort to a Python class,
> for correctly representing a Scheme closure containing functions meant to
> survive outside the scope of their definition?

Not in Python 2.2.  In Python 2.1, you could also get such lexically nested
scopes, but you needed a from __future__ import nested_scopes to enable 
that behavior.


> I did not study what happened to scopes since Python 1.5.2, I know that
> there are changes in this area, so maybe the problem I see is already
> gone.

Yes.  In 1.5.2, you needed to be a bit more explicit to carry state.  A 
class remains the typical and most general way to carry state, and we 
always had other ways, e.g. the default-arguments of a function.

Here, of course, plain default arguments wouldn't work because of the 
mutual dependency:

 def modify(redo, undo, focuses):
     def redo_function(undo_function=undo_function):
         redo()
         return undo_function, focuses
     def undo_function(redo_function=redo_function):
         undo()
         return redo_function, focuses

the first nested def fails since undo_function is yet unbound then.  To
avoid a class, it would take a wee little bit of trickiness, e.g.:

 def modify(redo, undo, focuses):
     temp=[None]
     def redo_function(undo_function=temp):
         redo()
         return undo_function[0], focuses
     def undo_function(redo_function=redo_function):
         undo()
         return redo_function, focuses
     temp[0] = undo_function

or the like.  A class being clearer at this point, of course.


The way this kind of stuff now "just works" is a testament to nested scopes 
as currently implemented (although they may not be perfect, e.g. you still
can't rebind free variables but can hide them, but many of us had more
qualms back when they were introduced).


>> def undo():
>>     if len(undo_list) == 0:
>>         warning(_("No more undo!"))
>>     redo_fn, focus_list[:] = undo_list.pop()()
>>     redo_list.append(redo_fn)
> 
> I'm only worried a bit by the replacement of the whole focus_list, not
> knowing how efficient such an operation is.  But even if it was slow,
> it would be bearable in this case.

It's reasonably efficient -- I'm not sure what your worry is.


>> def replace(value, focuses):
>>     restore = focuses[0]
>>     if len(focuses) == 1:
>>         def redo():  focuses[0] = value
>>         def undo():  focuses[0] = restore
>>         modify(redo, undo, focuses)
> 
> I did not study all the implications in detail, but yet, wonder if the
> `redo' and `undo' function correctly photograph the current value of
> `focuses', at
> the time of the definition occurred, for later use.  I would be tempted to
> doubt it.  Especially given that slice replacement is used in other
> places.

I haven't followed the functions in detail, but, generally speaking, Python 
does no implicit copying: if you need a "snapshot" of how a list looks at
a given time you copy it explicitly.


> I still have the overall feeling that, without special care, it is not
> that easy to write Python code that correctly represents the original
> Scheme. It might require more vertical space, including a few more
> accessory classes.

Translating between very different languages does often take special
care, of course.  Classes are always a very natural way to represent
some code plus some data in Python, although with lexical scopes and
generators there are now a lot of alternatives in various situations.


Alex




More information about the Python-list mailing list