In defence of the two-namespace rule

Bernhard Herzog herzog at online.de
Thu Jan 20 12:03:04 EST 2000


Edward Welbourne <eddyw at lsl.co.uk> writes:

> Markus Stenberg and Bernhard Herzog consider the two-namespace rule to
> be a deficiency in python, and think it would be `simpler' to use
> `lexical scoping'.

I think I should clarify my position. I don't think that the
two-namespace rule is a deficiency. I have never really needed more than
that in all the Python programs I wrote. That may be simply be because I
have more of an OOP background (mostly C++ before I discovered Python)
than functional, so I tend to use class instances or bound methods where
some other people want to use curried functions, etc.

However, several people have proposed lexical scoping extensions and
some of these proposals have even been implemented. Lexical scoping is
somewaht controversial and I don't know what Guido's opinion is,
although if the technical reasons (cyclic references) Tim mentioned in
his post were the only reason Guido dropped it, it seems likely that
lexical scoping might be implemented if the technical problems can be
solved.

Lexical scoping would make your tunneling feature unnecessary.
Therefore, IMO, if there's a reasonable chance that Python 2 has lexical
scoping there's no real need to have tunneling at all.

>  The Tim has given an excellent and erudite summary
> of what that could be asked to mean, since Evan asked and its advocates
> didn't answer.  Personally, I consider the two-namespace rule to be a
> major *strength* of python: it is a major ingredient in pythonicness.
> 
> Define a function, foo, which returns a function it has defined, as bar,
> during its suite; have bar refer to some of the local variables of foo.
> With lexical scoping, the interpreter is obliged to keep the namespace
> foo used (on that invocation) for as long as anyone retains a reference
> to what it returned.
> 
> I contend that the namespace in which a function executes its suite
> *should* be discardable at the time the function returns.

That is indeed a useful feature because it makes it easy to predict when
objects are collected in most cases (assuming refcounting).

>  Lexical
> scoping would mean that we can't discard foo's namespace when foo
> finishes execution.  The implications of this for garbage collection are
> singularly unpleasant - give them some thought.

It seems to me that in most cases there are only a few names of f's
namespace that have to be presevered and the compiler can easily tell
which. Of course they can still introduce cyclic references especially
if you want bar to be recursive.


> The only way out is for the values bar accesses from foo to be
> transcribed, before foo finishes executing, into some namespace whose
> lifespan is at least as long as that of bar (and, ideally, coeval with
> it).

If lexical scoping is not adopted this might be useful.

>  Since bar carries around a namespace, for its defaults, with
> exactly the right life-span, that's the right place to which to
> transcribe such values (promoting them to globals being, I trust, to
> no-one's liking).  Now, some folk might prefer to have that done
> implicitly: personally, I'm all in favour of each suite of code needing
> to be explicit about its dependencies on others.

Yes, if such a mechanism is added it should be explicit and not implicit.

>  Like Evan,
> 
> > I rather like the idea of having to explicitly bring names into an
> > inner scope, and with semantics identical to parameter defaults.

I think what this is about is not bringing names into the inner scope
but values.

And to Evan's next two questions:

> Why is this so objectionable? Is it just the syntax?

IMO, it's the syntax, mostly. The argument list is the wrong place.
Arguments are used when the funtion is called, not when it's defined.

To reuse one of Edward's examples:

  def filename(*fragments, **, root=os.getcwd()):
      """Resolves relative paths."""
      # Assume os.path.join joins arbitrarily many fragments.
      return apply(os.path.join, (root,) + fragments)

IMO, the root=os.getcwd() should be moved outside the argument list.
It's assigned at definition time and can't be changed in any way when
the function is called, so it's not an argument. Something like this
would be better:

  def filename(*fragments), root=os.getcwd():
      """Resolves relative paths."""
      # Assume os.path.join joins arbitrarily many fragments.
      return apply(os.path.join, (root,) + fragments)


-- 
Bernhard Herzog   | Sketch, a drawing program for Unix
herzog at online.de  | http://sketch.sourceforge.net/



More information about the Python-list mailing list